mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
<!-- Please provide as much information as possible about what your PR aims to do. PRs with no description will most likely be closed until more information is provided. If you're planing on changing fundamental behaviour or add big new features, please open a GitHub Issue first before starting to work on it. If it's not something big and you still want to contact us about it, feel free to do so ! --> ### Problem description This is a fix for the ImHex bug "ImHex crashes when analysing any PE file #2221" ### Implementation description This is a fix for the ImHex bug "ImHex crashes when analysing any PE file #2221". The fix requires changes to the Pattern Language and the ImHex UI. Revision would be wise. We want to avoid collateral damage. It's a big code base and I'm new to it and the compilers/build-systems used. And the bug is complex and low-level. I suspect this will fix other random crashes. The problem is caused by two issues: - The std::sort algorithm conjuring up garbage due to the sorting criteria not being a strict weak ordering. See [this](https://github.com/Voultapher/sort-research-rs/blob/main/writeup/sort_safety/text.md) link. - We sort shared_ptr<ptrn::Pattern> by pointer value, and the object is a clone. In essence we're changing the values as we're sorting. Fixes #2221 ### IMPORTANT I'm not sure how "plugins/builtin/source/content/data_formatters.cpp" got into the PR. Been trying for an hour to rectify. I'm not a Git expert (the last time I used source control seriously SourceSafe was a thing) please ignore that file. It's a fix for another PR I submitted. I suspect I stuffed up the branching and merging. --------- Co-authored-by: Nik <werwolv98@gmail.com>
224 lines
10 KiB
C++
224 lines
10 KiB
C++
#include <hex/api/content_registry.hpp>
|
|
#include <hex/api/achievement_manager.hpp>
|
|
|
|
#include <hex/providers/provider.hpp>
|
|
#include <hex/providers/buffered_reader.hpp>
|
|
|
|
#include <hex/helpers/fmt.hpp>
|
|
#include <hex/helpers/crypto.hpp>
|
|
#include <hex/helpers/utils.hpp>
|
|
|
|
#include <content/export_formatters/export_formatter_csv.hpp>
|
|
#include <content/export_formatters/export_formatter_tsv.hpp>
|
|
#include <content/export_formatters/export_formatter_json.hpp>
|
|
|
|
namespace hex::plugin::builtin {
|
|
|
|
static std::string formatLanguageArray(prv::Provider *provider, u64 offset, size_t size, const std::string &start, const std::string &byteFormat, const std::string &end, bool removeFinalDelimiter = false, bool newLines = true) {
|
|
constexpr static auto NewLineIndent = "\n ";
|
|
constexpr static auto LineLength = 16;
|
|
|
|
std::string result;
|
|
result.reserve(start.size() + hex::format(byteFormat, 0x00).size() * size + std::string(NewLineIndent).size() / LineLength + end.size());
|
|
|
|
result += start;
|
|
|
|
auto reader = prv::ProviderReader(provider);
|
|
reader.seek(offset);
|
|
reader.setEndAddress(offset + size - 1);
|
|
|
|
u64 index = 0x00;
|
|
for (u8 byte : reader) {
|
|
|
|
if (newLines) {
|
|
if ((index % LineLength) == 0x00)
|
|
result += NewLineIndent;
|
|
}
|
|
|
|
result += hex::format(byteFormat, byte);
|
|
|
|
index++;
|
|
}
|
|
|
|
// Remove trailing delimiter if required
|
|
if (removeFinalDelimiter && size > 0) {
|
|
result.pop_back();
|
|
result.pop_back();
|
|
}
|
|
|
|
if (newLines) result += "\n";
|
|
result += end;
|
|
|
|
return result;
|
|
}
|
|
|
|
void registerDataFormatters() {
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.c", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, hex::format("const uint8_t data[{0}] = {{", size), "0x{0:02X}, ", "};");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.cpp", [](prv::Provider *provider, u64 offset, size_t size, bool preview) {
|
|
if (!preview) {
|
|
AchievementManager::unlockAchievement("hex.builtin.achievement.hex_editor", "hex.builtin.achievement.hex_editor.copy_as.name");
|
|
}
|
|
|
|
return formatLanguageArray(provider, offset, size, hex::format("constexpr std::array<uint8_t, {0}> data = {{", size), "0x{0:02X}, ", "};");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.java", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, "final byte[] data = {", "0x{0:02X}, ", "};");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.csharp", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, "const byte[] data = {", "0x{0:02X}, ", "};");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.rust", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, hex::format("let data: [u8; 0x{0:02X}] = [", size), "0x{0:02X}, ", "];");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.python", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, "data = bytes([", "0x{0:02X}, ", "])");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.js", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, "const data = new Uint8Array([", "0x{0:02X}, ", "]);");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.lua", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, "data = {", "0x{0:02X}, ", "}");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.go", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, "data := [...]byte{", "0x{0:02X}, ", "}", false);
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.crystal", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, "data = [", "0x{0:02X}, ", "] of UInt8");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.swift", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, "let data: [Uint8] = [", "0x{0:02X}, ", "]");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.pascal", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, hex::format("data: array[0..{0}] of Byte = (", size - 1), "${0:02X}, ", ")");
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.base64", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
std::vector<u8> data(size, 0x00);
|
|
provider->read(offset, data.data(), size);
|
|
|
|
auto result = crypt::encode64(data);
|
|
|
|
return std::string(result.begin(), result.end());
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.hex_view", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return hex::generateHexView(offset, size, provider);
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.html", [](prv::Provider *provider, u64 offset, size_t size, bool preview) {
|
|
// Don't display a preview for this formatter as it wouldn't make much sense either way.
|
|
if (preview)
|
|
return std::string();
|
|
|
|
std::string result =
|
|
"<div>\n"
|
|
" <style type=\"text/css\">\n"
|
|
" .offsetheader { color:#0000A0; line-height:200% }\n"
|
|
" .offsetcolumn { color:#0000A0 }\n"
|
|
" .hexcolumn { color:#000000 }\n"
|
|
" .textcolumn { color:#000000 }\n"
|
|
" .zerobyte { color:#808080 }\n"
|
|
" </style>\n\n"
|
|
" <code>\n"
|
|
" <span class=\"offsetheader\">Hex View 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F</span>";
|
|
|
|
auto html_safe = [](u8 byte) -> std::string {
|
|
if (!std::isprint(byte))
|
|
return ".";
|
|
char b(byte);
|
|
if (b==' ') return " ";
|
|
else if (b=='"') return """;
|
|
else if (b=='\'') return "'";
|
|
else if (b=='&') return "&";
|
|
else if (b=='<') return "<";
|
|
else if (b=='>') return ">";
|
|
else return std::string{b};
|
|
};
|
|
|
|
auto reader = prv::ProviderReader(provider);
|
|
reader.seek(offset);
|
|
reader.setEndAddress((offset + size) - 1);
|
|
|
|
u64 address = offset & ~u64(0x0F);
|
|
|
|
std::string asciiRow;
|
|
for (u8 byte : reader) {
|
|
if ((address % 0x10) == 0) {
|
|
result += hex::format(" {}", asciiRow);
|
|
result += hex::format("<br>\n <span class=\"offsetcolumn\">{0:08X}</span> <span class=\"hexcolumn\">", address);
|
|
|
|
asciiRow.clear();
|
|
|
|
if (address == (offset & ~u64(0x0F))) {
|
|
for (u64 i = 0; i < (offset - address); i++) {
|
|
result += " ";
|
|
asciiRow += " ";
|
|
}
|
|
address = offset;
|
|
}
|
|
|
|
result += "</span>";
|
|
}
|
|
|
|
std::string tagStart, tagEnd;
|
|
if (byte == 0x00) {
|
|
tagStart = "<span class=\"zerobyte\">";
|
|
tagEnd = "</span>";
|
|
}
|
|
|
|
result += hex::format("{0}{2:02X}{1} ", tagStart, tagEnd, byte);
|
|
asciiRow += html_safe(byte);
|
|
if ((address % 0x10) == 0x07)
|
|
result += " ";
|
|
|
|
address++;
|
|
}
|
|
|
|
if (address % 0x10 != 0x00)
|
|
for (u32 i = 0; i < (0x10 - (address % 0x10)); i++)
|
|
result += " ";
|
|
result += asciiRow;
|
|
|
|
result +=
|
|
"\n </code>\n"
|
|
"</div>\n";
|
|
|
|
return result;
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addExportMenuEntry("hex.builtin.view.hex_editor.copy.escaped_string", [](prv::Provider *provider, u64 offset, size_t size, bool) {
|
|
return formatLanguageArray(provider, offset, size, "\"", "\\x{0:02X}", "\"", false, false);
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addFindExportFormatter("csv", "csv", [](const std::vector<ContentRegistry::DataFormatter::impl::FindOccurrence>& occurrences, const auto &transformFunc) {
|
|
export_fmt::ExportFormatterCsv formatter;
|
|
return formatter.format(occurrences, transformFunc);
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addFindExportFormatter("tsv", "tsv", [](const std::vector<ContentRegistry::DataFormatter::impl::FindOccurrence>& occurrences, const auto &transformFunc) {
|
|
export_fmt::ExportFormatterTsv formatter;
|
|
return formatter.format(occurrences, transformFunc);
|
|
});
|
|
|
|
ContentRegistry::DataFormatter::addFindExportFormatter("json", "json", [](const std::vector<ContentRegistry::DataFormatter::impl::FindOccurrence>& occurrences, const auto &transformFunc) {
|
|
export_fmt::ExportFormatterJson formatter;
|
|
return formatter.format(occurrences, transformFunc);
|
|
});
|
|
}
|
|
|
|
}
|