diff --git a/lib/libimhex/include/hex/helpers/utils.hpp b/lib/libimhex/include/hex/helpers/utils.hpp index 2deef52d8..98a12a7e0 100644 --- a/lib/libimhex/include/hex/helpers/utils.hpp +++ b/lib/libimhex/include/hex/helpers/utils.hpp @@ -326,6 +326,8 @@ namespace hex { return remainder != 0 ? value + (alignment - remainder) : value; } + std::optional hexCharToValue(char c); + bool isProcessElevated(); std::optional getEnvironmentVariable(const std::string &env); diff --git a/lib/libimhex/source/helpers/utils.cpp b/lib/libimhex/source/helpers/utils.cpp index 177266fbd..a45cd8e37 100644 --- a/lib/libimhex/source/helpers/utils.cpp +++ b/lib/libimhex/source/helpers/utils.cpp @@ -280,6 +280,15 @@ namespace hex { #endif } + std::optional hexCharToValue(char c) { + if (std::isdigit(c)) + return c - '0'; + else if (std::isxdigit(c)) + return std::toupper(c) - 'A' + 0x0A; + else + return { }; + } + std::string encodeByteString(const std::vector &bytes) { std::string result; @@ -370,12 +379,8 @@ namespace hex { for (u8 i = 0; i < 2; i++) { byte <<= 4; - if (c() >= '0' && c() <= '9') - byte |= 0x00 + (c() - '0'); - else if (c() >= 'A' && c() <= 'F') - byte |= 0x0A + (c() - 'A'); - else if (c() >= 'a' && c() <= 'f') - byte |= 0x0A + (c() - 'a'); + if (auto hexValue = hexCharToValue(c()); hexValue.has_value()) + byte |= hexValue.value(); else return {}; diff --git a/plugins/builtin/include/content/views/view_find.hpp b/plugins/builtin/include/content/views/view_find.hpp index afd6bc2d7..bdefb9e4b 100644 --- a/plugins/builtin/include/content/views/view_find.hpp +++ b/plugins/builtin/include/content/views/view_find.hpp @@ -19,13 +19,18 @@ namespace hex::plugin::builtin { private: + struct BinaryPattern { + u8 mask, value; + }; + struct SearchSettings { int range = 0; enum class Mode : int { Strings, Sequence, - Regex + Regex, + BinaryPattern } mode = Mode::Strings; struct Strings { @@ -49,6 +54,11 @@ namespace hex::plugin::builtin { struct Regex { std::string pattern; } regex; + + struct BinaryPattern { + std::string input; + std::vector pattern; + } binaryPattern; } m_searchSettings, m_decodeSettings; std::map> m_foundRegions, m_sortedRegions; @@ -60,6 +70,9 @@ namespace hex::plugin::builtin { static std::vector searchStrings(Task &&task, prv::Provider *provider, Region searchRegion, SearchSettings::Strings settings); static std::vector searchSequence(Task &&task, prv::Provider *provider, Region searchRegion, SearchSettings::Bytes settings); static std::vector searchRegex(Task &&task, prv::Provider *provider, Region searchRegion, SearchSettings::Regex settings); + static std::vector searchBinaryPattern(Task &&task, prv::Provider *provider, Region searchRegion, SearchSettings::BinaryPattern settings); + + static std::vector parseBinaryPatternString(std::string string); void runSearch(); std::string decodeValue(prv::Provider *provider, Region region); diff --git a/plugins/builtin/source/content/views/view_find.cpp b/plugins/builtin/source/content/views/view_find.cpp index 88344ae71..74a4957ca 100644 --- a/plugins/builtin/source/content/views/view_find.cpp +++ b/plugins/builtin/source/content/views/view_find.cpp @@ -100,6 +100,63 @@ namespace hex::plugin::builtin { }); } + + std::vector ViewFind::parseBinaryPatternString(std::string string) { + std::vector result; + + if (string.length() < 2) + return { }; + + bool inString = false; + while (string.length() > 0) { + BinaryPattern pattern = { 0, 0 }; + if (string.starts_with("\"")) { + inString = !inString; + string = string.substr(1); + continue; + } else if (inString) { + pattern = { 0xFF, u8(string.front()) }; + string = string.substr(1); + } else if (string.starts_with("??")) { + pattern = { 0x00, 0x00 }; + string = string.substr(2); + } else if ((std::isxdigit(string.front()) || string.front() == '?') && string.length() >= 2) { + const auto hex = string.substr(0, 2); + + for (const auto &c : hex) { + pattern.mask <<= 4; + pattern.value <<= 4; + + if (std::isxdigit(c)) { + pattern.mask |= 0x0F; + + if (auto hexValue = hex::hexCharToValue(c); hexValue.has_value()) + pattern.value |= hexValue.value(); + else + return { }; + } else if (c != '?') { + return { }; + } + } + + string = string.substr(2); + } else if (std::isspace(string.front())) { + string = string.substr(1); + continue; + } else { + return { }; + } + + result.push_back(pattern); + } + + if (inString) + return { }; + + return result; + } + + std::vector ViewFind::searchStrings(Task &&task, prv::Provider *provider, hex::Region searchRegion, SearchSettings::Strings settings) { std::vector results; @@ -196,6 +253,34 @@ namespace hex::plugin::builtin { return result; } + std::vector ViewFind::searchBinaryPattern(Task &&task, prv::Provider *provider, hex::Region searchRegion, SearchSettings::BinaryPattern settings) { + std::vector results; + + auto reader = prv::BufferedReader(provider); + reader.seek(searchRegion.getStartAddress()); + reader.setEndAddress(searchRegion.getEndAddress()); + + u32 matchedBytes = 0; + u64 address = searchRegion.getStartAddress(); + const size_t patternSize = settings.pattern.size(); + for (u8 byte : reader) { + if ((byte & settings.pattern[matchedBytes].mask) == settings.pattern[matchedBytes].value) { + matchedBytes++; + if (matchedBytes == settings.pattern.size()) { + results.push_back(Region { address - (patternSize - 1), patternSize }); + task.update(address); + matchedBytes = 0; + } + } else { + matchedBytes = 0; + } + + address++; + } + + return results; + } + void ViewFind::runSearch() { Region searchRegion = [this]{ if (this->m_searchSettings.range == 0 || !ImHexApi::HexEditor::isSelectionValid()) { @@ -222,7 +307,10 @@ namespace hex::plugin::builtin { case Regex: this->m_foundRegions[provider] = searchRegex(std::move(task), provider, searchRegion, settings.regex); break; - } + case BinaryPattern: + this->m_foundRegions[provider] = searchBinaryPattern(std::move(task), provider, searchRegion, settings.binaryPattern); + break; + } this->m_sortedRegions = this->m_foundRegions; this->m_searchRunning = false; @@ -259,6 +347,7 @@ namespace hex::plugin::builtin { break; case Sequence: case Regex: + case BinaryPattern: result = hex::encodeByteString(bytes); break; } @@ -361,6 +450,18 @@ namespace hex::plugin::builtin { ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("hex.builtin.view.find.binary_pattern"_lang)) { + auto &settings = this->m_searchSettings.binaryPattern; + + mode = SearchSettings::Mode::BinaryPattern; + + ImGui::InputText("hex.builtin.view.find.binary_pattern"_lang, settings.input); + + settings.pattern = parseBinaryPatternString(settings.input); + this->m_settingsValid = !settings.pattern.empty(); + + ImGui::EndTabItem(); + } ImGui::EndTabBar(); } diff --git a/plugins/builtin/source/lang/de_DE.cpp b/plugins/builtin/source/lang/de_DE.cpp index 01b9203eb..e7da88498 100644 --- a/plugins/builtin/source/lang/de_DE.cpp +++ b/plugins/builtin/source/lang/de_DE.cpp @@ -402,6 +402,7 @@ namespace hex::plugin::builtin { { "hex.builtin.view.find.strings.line_feeds", "Line Feeds" }, { "hex.builtin.view.find.sequences", "Sequenzen" }, { "hex.builtin.view.find.regex", "Regex" }, + { "hex.builtin.view.find.binary_pattern", "Binärpattern" }, { "hex.builtin.view.find.search", "Suchen" }, { "hex.builtin.view.find.context.copy", "Wert Kopieren" }, { "hex.builtin.view.find.context.copy_demangle", "Demangled Wert Kopieren" }, diff --git a/plugins/builtin/source/lang/en_US.cpp b/plugins/builtin/source/lang/en_US.cpp index 11bae4665..9face57b6 100644 --- a/plugins/builtin/source/lang/en_US.cpp +++ b/plugins/builtin/source/lang/en_US.cpp @@ -406,6 +406,7 @@ namespace hex::plugin::builtin { { "hex.builtin.view.find.strings.line_feeds", "Line Feeds" }, { "hex.builtin.view.find.sequences", "Sequences" }, { "hex.builtin.view.find.regex", "Regex" }, + { "hex.builtin.view.find.binary_pattern", "Binary Pattern" }, { "hex.builtin.view.find.search", "Search" }, { "hex.builtin.view.find.context.copy", "Copy Value" }, { "hex.builtin.view.find.context.copy_demangle", "Copy Demangled Value" }, diff --git a/plugins/builtin/source/lang/it_IT.cpp b/plugins/builtin/source/lang/it_IT.cpp index 79394bbaa..b1325f95a 100644 --- a/plugins/builtin/source/lang/it_IT.cpp +++ b/plugins/builtin/source/lang/it_IT.cpp @@ -405,6 +405,7 @@ namespace hex::plugin::builtin { // { "hex.builtin.view.find.strings.line_feeds", "Line Feeds" }, // { "hex.builtin.view.find.sequences", "Sequences" }, // { "hex.builtin.view.find.regex", "Regex" }, + // { "hex.builtin.view.find.binary_pattern", "Binary Pattern" }, // { "hex.builtin.view.find.search", "Search" }, // { "hex.builtin.view.find.context.copy", "Copy Value" }, // { "hex.builtin.view.find.context.copy_demangle", "Copy Demangled Value" }, diff --git a/plugins/builtin/source/lang/ja_JP.cpp b/plugins/builtin/source/lang/ja_JP.cpp index 9c7204f16..43996265c 100644 --- a/plugins/builtin/source/lang/ja_JP.cpp +++ b/plugins/builtin/source/lang/ja_JP.cpp @@ -406,6 +406,7 @@ namespace hex::plugin::builtin { // { "hex.builtin.view.find.strings.line_feeds", "Line Feeds" }, // { "hex.builtin.view.find.sequences", "Sequences" }, // { "hex.builtin.view.find.regex", "Regex" }, + // { "hex.builtin.view.find.binary_pattern", "Binary Pattern" }, // { "hex.builtin.view.find.search", "Search" }, // { "hex.builtin.view.find.context.copy", "Copy Value" }, // { "hex.builtin.view.find.context.copy_demangle", "Copy Demangled Value" }, diff --git a/plugins/builtin/source/lang/pt_BR.cpp b/plugins/builtin/source/lang/pt_BR.cpp index 3886af6c3..2d0a09886 100644 --- a/plugins/builtin/source/lang/pt_BR.cpp +++ b/plugins/builtin/source/lang/pt_BR.cpp @@ -403,6 +403,7 @@ namespace hex::plugin::builtin { // { "hex.builtin.view.find.strings.line_feeds", "Line Feeds" }, // { "hex.builtin.view.find.sequences", "Sequences" }, // { "hex.builtin.view.find.regex", "Regex" }, + // { "hex.builtin.view.find.binary_pattern", "Binary Pattern" }, // { "hex.builtin.view.find.search", "Search" }, // { "hex.builtin.view.find.context.copy", "Copy Value" }, // { "hex.builtin.view.find.context.copy_demangle", "Copy Demangled Value" }, diff --git a/plugins/builtin/source/lang/zh_CN.cpp b/plugins/builtin/source/lang/zh_CN.cpp index fad37fbcc..5c5d0d7f5 100644 --- a/plugins/builtin/source/lang/zh_CN.cpp +++ b/plugins/builtin/source/lang/zh_CN.cpp @@ -406,6 +406,7 @@ namespace hex::plugin::builtin { // { "hex.builtin.view.find.strings.line_feeds", "Line Feeds" }, // { "hex.builtin.view.find.sequences", "Sequences" }, // { "hex.builtin.view.find.regex", "Regex" }, + // { "hex.builtin.view.find.binary_pattern", "Binary Pattern" }, // { "hex.builtin.view.find.search", "Search" }, // { "hex.builtin.view.find.context.copy", "Copy Value" }, // { "hex.builtin.view.find.context.copy_demangle", "Copy Demangled Value" }, diff --git a/plugins/builtin/source/lang/zh_TW.cpp b/plugins/builtin/source/lang/zh_TW.cpp index 51ac2ddb6..3ca5fc4ad 100644 --- a/plugins/builtin/source/lang/zh_TW.cpp +++ b/plugins/builtin/source/lang/zh_TW.cpp @@ -404,6 +404,7 @@ namespace hex::plugin::builtin { // { "hex.builtin.view.find.strings.line_feeds", "Line Feeds" }, // { "hex.builtin.view.find.sequences", "Sequences" }, // { "hex.builtin.view.find.regex", "Regex" }, + // { "hex.builtin.view.find.binary_pattern", "Binary Pattern" }, // { "hex.builtin.view.find.search", "Search" }, // { "hex.builtin.view.find.context.copy", "Copy Value" }, // { "hex.builtin.view.find.context.copy_demangle", "Copy Demangled Value" },