mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-05 21:56:33 +00:00
119 lines
5.0 KiB
C++
119 lines
5.0 KiB
C++
#include "resources.h"
|
|
|
|
#include <fstream>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
|
|
static void check(bool condition, const char *message)
|
|
{
|
|
if (!condition)
|
|
throw std::runtime_error(message);
|
|
}
|
|
|
|
static std::vector<uint8_t> read_file(const char *path)
|
|
{
|
|
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
|
if (!file || file.tellg() < 0 || file.tellg() > 1024 * 1024 * 1024)
|
|
throw std::runtime_error("invalid resource fixture");
|
|
std::vector<uint8_t> bytes(static_cast<size_t>(file.tellg()));
|
|
file.seekg(0);
|
|
if (!file.read(reinterpret_cast<char *>(bytes.data()), static_cast<std::streamsize>(bytes.size())))
|
|
throw std::runtime_error("cannot read resource fixture");
|
|
return bytes;
|
|
}
|
|
|
|
static void rejected(const std::function<void()> &operation, std::string_view reason)
|
|
{
|
|
try {
|
|
operation();
|
|
} catch (const std::runtime_error &error) {
|
|
check(std::string_view(error.what()).find(reason) != std::string_view::npos, "resource negative failed for a different reason");
|
|
return;
|
|
}
|
|
throw std::runtime_error("malformed resource metadata was accepted");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
try {
|
|
check(argc == 3, "PE64 and PE32 resource fixtures required");
|
|
for (int file = 1; file < argc; ++file) {
|
|
const auto bytes = read_file(argv[file]);
|
|
const raven::PEImage image(bytes);
|
|
const auto resources = image.resources();
|
|
check(image.is_64 == (file == 1) && resources.size() == 6, "compiled resource architecture/count differs");
|
|
bool custom = false, mixed = false, french = false, neutral = false;
|
|
for (const auto &resource : resources) {
|
|
check(resource.size == 4 && image.file_span(resource.data_address, 4).size() == 4,
|
|
"compiled resource payload is not file-backed");
|
|
custom |= resource.type == raven::ResourceKey(std::u16string(u"CUSTOM")) && resource.name == raven::ResourceKey(uint32_t(7));
|
|
mixed |= resource.type == raven::ResourceKey(uint32_t(10)) && resource.name == raven::ResourceKey(std::u16string(u"MIXED"));
|
|
french |= resource.name == raven::ResourceKey(uint32_t(101)) && resource.language == 0x40c;
|
|
neutral |= resource.name == raven::ResourceKey(uint32_t(102)) && resource.language == 0;
|
|
}
|
|
check(custom && mixed && french && neutral, "compiled resource identities differ");
|
|
const auto directory = image.directories[2];
|
|
const auto original = image.file_span(directory.address, directory.size);
|
|
std::vector<uint8_t> altered(original.begin(), original.end());
|
|
const auto put = [&](size_t offset, uint32_t value, size_t width = 4) {
|
|
for (size_t index = 0; index < width; ++index)
|
|
altered.at(offset + index) = static_cast<uint8_t>(value >> (index * 8));
|
|
};
|
|
const auto parse = [&] { raven::read_resources(directory, altered, image.image_size); };
|
|
put(12, 0xffff, 2);
|
|
rejected(parse, "exceeds directory");
|
|
altered.assign(original.begin(), original.end());
|
|
put(16, 0x80000000U | (directory.size + 2));
|
|
rejected(parse, "exceeds directory");
|
|
altered.assign(original.begin(), original.end());
|
|
put(20, 0x80000000U);
|
|
rejected(parse, "reused");
|
|
altered.assign(original.begin(), original.end());
|
|
put(20, 0x80000001U);
|
|
rejected(parse, "unaligned");
|
|
altered.assign(original.begin(), original.end());
|
|
put(20, 0);
|
|
rejected(parse, "before language");
|
|
altered.assign(original.begin(), original.end());
|
|
put(16, 7);
|
|
rejected(parse, "partitions");
|
|
altered.assign(original.begin(), original.end());
|
|
const size_t leaf = resources[0].entry_address - directory.address;
|
|
put(leaf, image.image_size + 1);
|
|
rejected(parse, "payload exceeds image");
|
|
altered.assign(original.begin(), original.end());
|
|
put(leaf + 4, UINT32_MAX);
|
|
rejected(parse, "payload exceeds image");
|
|
altered.assign(original.begin(), original.end());
|
|
put(leaf + 4, 0);
|
|
check(raven::read_resources(directory, altered, image.image_size)[0].size == 0,
|
|
"zero-length resource metadata was rejected");
|
|
altered.assign(original.begin(), original.end());
|
|
const auto read32 = [&](size_t offset) {
|
|
uint32_t value = 0;
|
|
for (size_t index = 0; index < 4; ++index)
|
|
value |= uint32_t(original[offset + index]) << (index * 8);
|
|
return value;
|
|
};
|
|
const size_t type = read32(28) & 0x7fffffffU;
|
|
put(type + 32, 101);
|
|
rejected(parse, "unordered or duplicated");
|
|
altered.assign(original.begin(), original.end());
|
|
const size_t languages = read32(type + 28) & 0x7fffffffU;
|
|
put(languages + 20, 0x80000000U);
|
|
rejected(parse, "language leaf");
|
|
// IDs are stored in 32 bits, independently of MAKEINTRESOURCE's argument width.
|
|
altered.assign(original.begin(), original.end());
|
|
put(24, 70000);
|
|
const auto wide_id = raven::read_resources(directory, altered, image.image_size);
|
|
check(wide_id.back().type == raven::ResourceKey(uint32_t(70000)), "resource ID was truncated to WORD");
|
|
}
|
|
std::cout << "Compiled PE32/PE64 resource identities, payloads and malformed tree contracts passed\n";
|
|
return 0;
|
|
} catch (const std::exception &error) {
|
|
std::cerr << error.what() << '\n';
|
|
return 1;
|
|
}
|
|
}
|