#include "png_codec.h" #include "pe/resources.h" #include "pe/loaded_image.h" #include #include #include #include #include #include #include #include static void check(bool condition, const char *message) { if (!condition) throw std::runtime_error(message); } static std::vector read_file(const std::filesystem::path &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 PNG test input"); std::vector bytes(static_cast(file.tellg())); file.seekg(0); if (!file.read(reinterpret_cast(bytes.data()), static_cast(bytes.size()))) throw std::runtime_error("cannot read PNG test input"); return bytes; } static void rejected(const std::function &operation, std::string_view reason) { std::string failure; try { operation(); } catch (const std::runtime_error &error) { failure = error.what(); } if (failure.empty() || failure.find(reason) == failure.npos) throw std::runtime_error("PNG negative expected " + std::string(reason) + "; observed: " + failure); } int main(int argc, char **argv) { try { check(argc == 3, "application and PNG output directory required"); const uint8_t known[] = {137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 3, 0, 0, 0, 2, 8, 6, 0, 0, 0, 157, 116, 102, 26, 0, 0, 0, 31, 73, 68, 65, 84, 120, 156, 99, 248, 207, 192, 240, 31, 8, 27, 64, 20, 3, 167, 128, 3, 51, 35, 19, 243, 191, 255, 255, 255, 51, 2, 0, 108, 149, 8, 219, 131, 137, 245, 62, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130}; const std::vector expected{255, 0, 0, 255, 0, 255, 0, 128, 0, 0, 255, 0, 9, 16, 64, 3, 1, 2, 3, 254, 255, 255, 255, 1}; const auto sample = raven::decode_png(known); check(sample.width == 3 && sample.height == 2 && sample.rgba == expected, "RGBA channels, row order or straight alpha differ"); const auto bytes = read_file(argv[1]); const raven::PEImage image(bytes); const auto resources = image.resources(); raven::LoadedImage loaded(image, image.image_base); const std::filesystem::path directory(argv[2]); std::filesystem::create_directories(directory); unsigned decoded = 0; std::vector first; for (const auto &resource : resources) { if (resource.type != raven::ResourceKey(uint32_t(3))) continue; const auto data = std::span(loaded.data()).subspan(resource.data_address, resource.size); const std::array signature{137, 80, 78, 71, 13, 10, 26, 10}; if (data.size() < 8 || !std::equal(signature.begin(), signature.end(), data.begin())) continue; const auto pixels = raven::decode_png(data); check(pixels.rgba.size() == size_t(pixels.width) * pixels.height * 4, "decoded PNG dimensions differ"); const auto id = std::get(resource.name); std::ofstream output(directory / (std::to_string(id) + ".rgba"), std::ios::binary | std::ios::trunc); const uint32_t header[] = {pixels.width, pixels.height}; output.write(reinterpret_cast(header), sizeof(header)); output.write(reinterpret_cast(pixels.rgba.data()), static_cast(pixels.rgba.size())); check(bool(output), "cannot save decoded PNG evidence"); if (first.empty()) first.assign(data.begin(), data.end()); ++decoded; } check(decoded == 2029, "real application PNG corpus changed"); rejected([&] { raven::decode_png({}); }, "invalid PNG input"); rejected([&] { raven::decode_png(std::span(first).first(20)); }, "header decoding failed"); rejected([&] { raven::decode_png(std::span(first).first(first.size() / 2)); }, "pixel decoding failed"); auto corrupted = first; corrupted[16] = 0; corrupted[17] = 0; corrupted[18] = 0x20; corrupted[19] = 1; const auto crc = crc32(0, corrupted.data() + 12, 17); for (size_t index = 0; index < 4; ++index) corrupted[29 + index] = static_cast(crc >> ((3 - index) * 8)); rejected([&] { raven::decode_png(corrupted); }, "pixel policy limit"); corrupted = first; for (size_t index = 0; index < 4; ++index) { corrupted[16 + index] = static_cast(4097U >> ((3 - index) * 8)); corrupted[20 + index] = corrupted[16 + index]; } const auto large_crc = crc32(0, corrupted.data() + 12, 17); for (size_t index = 0; index < 4; ++index) corrupted[29 + index] = static_cast(large_crc >> ((3 - index) * 8)); rejected([&] { raven::decode_png(corrupted); }, "pixel policy limit"); corrupted = first; corrupted[29] ^= 1; rejected([&] { raven::decode_png(corrupted); }, "header decoding failed"); std::cout << "Pinned libpng decoded " << decoded << " real application icons; malformed and bounded-allocation controls passed\n"; return 0; } catch (const std::exception &error) { std::cerr << error.what() << '\n'; return 1; } }