feat: Added support for automatically loading patterns using binary pattern magic
This commit is contained in:
parent
df75218ecb
commit
e7b51a56a5
2
lib/external/pattern_language
vendored
2
lib/external/pattern_language
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 157436f55f681fc3b59264741cd9f49ec036cb65
|
Subproject commit 49f3fb30dcb3044a2bb535958b7b954c81ec23ef
|
103
lib/libimhex/include/hex/helpers/binary_pattern.hpp
Normal file
103
lib/libimhex/include/hex/helpers/binary_pattern.hpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <hex.hpp>
|
||||||
|
|
||||||
|
#include <hex/helpers/utils.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace hex {
|
||||||
|
|
||||||
|
class BinaryPattern {
|
||||||
|
public:
|
||||||
|
struct Pattern {
|
||||||
|
u8 mask, value;
|
||||||
|
};
|
||||||
|
|
||||||
|
BinaryPattern() = default;
|
||||||
|
explicit BinaryPattern(const std::string &pattern) : m_patterns(parseBinaryPatternString(pattern)) { }
|
||||||
|
|
||||||
|
[[nodiscard]] bool isValid() const { return !this->m_patterns.empty(); }
|
||||||
|
|
||||||
|
[[nodiscard]] bool matches(const std::vector<u8> &bytes) const {
|
||||||
|
if (bytes.size() < this->m_patterns.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < this->m_patterns.size(); i++) {
|
||||||
|
if (!this->matchesByte(bytes[i], i))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool matchesByte(u8 byte, u32 offset) const {
|
||||||
|
const auto &pattern = this->m_patterns[offset];
|
||||||
|
|
||||||
|
return (byte & pattern.mask) == pattern.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t getSize() const {
|
||||||
|
return this->m_patterns.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::vector<Pattern> parseBinaryPatternString(std::string string) {
|
||||||
|
std::vector<Pattern> result;
|
||||||
|
|
||||||
|
if (string.length() < 2)
|
||||||
|
return { };
|
||||||
|
|
||||||
|
bool inString = false;
|
||||||
|
while (string.length() > 0) {
|
||||||
|
Pattern 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;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::vector<Pattern> m_patterns;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <hex/api/task.hpp>
|
#include <hex/api/task.hpp>
|
||||||
#include <hex/ui/view.hpp>
|
#include <hex/ui/view.hpp>
|
||||||
|
#include <hex/helpers/binary_pattern.hpp>
|
||||||
#include <ui/widgets.hpp>
|
#include <ui/widgets.hpp>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@ -76,7 +77,7 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
struct BinaryPattern {
|
struct BinaryPattern {
|
||||||
std::string input;
|
std::string input;
|
||||||
std::vector<ViewFind::BinaryPattern> pattern;
|
hex::BinaryPattern pattern;
|
||||||
u32 alignment = 1;
|
u32 alignment = 1;
|
||||||
} binaryPattern;
|
} binaryPattern;
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include <hex/api/content_registry.hpp>
|
#include <hex/api/content_registry.hpp>
|
||||||
#include <hex/providers/provider.hpp>
|
#include <hex/providers/provider.hpp>
|
||||||
#include <hex/helpers/magic.hpp>
|
#include <hex/helpers/magic.hpp>
|
||||||
|
#include <hex/helpers/binary_pattern.hpp>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <pl/core/evaluator.hpp>
|
#include <pl/core/evaluator.hpp>
|
||||||
|
|
||||||
@ -20,6 +22,10 @@ namespace hex::plugin::builtin {
|
|||||||
ContentRegistry::PatternLanguage::addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) {
|
ContentRegistry::PatternLanguage::addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) {
|
||||||
return magic::isValidMIMEType(value);
|
return magic::isValidMIMEType(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ContentRegistry::PatternLanguage::addPragma("magic", [](pl::PatternLanguage&, const std::string &) {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -22,7 +22,7 @@ namespace hex::plugin::builtin {
|
|||||||
if (this->m_searchTask.isRunning())
|
if (this->m_searchTask.isRunning())
|
||||||
return { };
|
return { };
|
||||||
|
|
||||||
if (!this->m_occurrenceTree->overlapping({ address, address }).empty())
|
if (!this->m_occurrenceTree->overlapping({ address, address + size }).empty())
|
||||||
return HighlightColor();
|
return HighlightColor();
|
||||||
else
|
else
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@ -34,7 +34,7 @@ namespace hex::plugin::builtin {
|
|||||||
if (this->m_searchTask.isRunning())
|
if (this->m_searchTask.isRunning())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto occurrences = this->m_occurrenceTree->overlapping({ address, address });
|
auto occurrences = this->m_occurrenceTree->overlapping({ address, address + size });
|
||||||
if (occurrences.empty())
|
if (occurrences.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -93,62 +93,6 @@ namespace hex::plugin::builtin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<ViewFind::BinaryPattern> ViewFind::parseBinaryPatternString(std::string string) {
|
|
||||||
std::vector<BinaryPattern> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type, typename StorageType>
|
template<typename Type, typename StorageType>
|
||||||
static std::tuple<bool, std::variant<u64, i64, float, double>, size_t> parseNumericValue(const std::string &string) {
|
static std::tuple<bool, std::variant<u64, i64, float, double>, size_t> parseNumericValue(const std::string &string) {
|
||||||
static_assert(sizeof(StorageType) >= sizeof(Type));
|
static_assert(sizeof(StorageType) >= sizeof(Type));
|
||||||
@ -365,7 +309,7 @@ namespace hex::plugin::builtin {
|
|||||||
reader.seek(searchRegion.getStartAddress());
|
reader.seek(searchRegion.getStartAddress());
|
||||||
reader.setEndAddress(searchRegion.getEndAddress());
|
reader.setEndAddress(searchRegion.getEndAddress());
|
||||||
|
|
||||||
const size_t patternSize = settings.pattern.size();
|
const size_t patternSize = settings.pattern.getSize();
|
||||||
|
|
||||||
if (settings.alignment == 1) {
|
if (settings.alignment == 1) {
|
||||||
u32 matchedBytes = 0;
|
u32 matchedBytes = 0;
|
||||||
@ -373,9 +317,9 @@ namespace hex::plugin::builtin {
|
|||||||
auto byte = *it;
|
auto byte = *it;
|
||||||
|
|
||||||
task.update(it.getAddress());
|
task.update(it.getAddress());
|
||||||
if ((byte & settings.pattern[matchedBytes].mask) == settings.pattern[matchedBytes].value) {
|
if (settings.pattern.matchesByte(byte, matchedBytes)) {
|
||||||
matchedBytes++;
|
matchedBytes++;
|
||||||
if (matchedBytes == settings.pattern.size()) {
|
if (matchedBytes == settings.pattern.getSize()) {
|
||||||
auto occurrenceAddress = it.getAddress() - (patternSize - 1);
|
auto occurrenceAddress = it.getAddress() - (patternSize - 1);
|
||||||
|
|
||||||
results.push_back(Occurrence { Region { occurrenceAddress, patternSize }, Occurrence::DecodeType::Binary, std::endian::native });
|
results.push_back(Occurrence { Region { occurrenceAddress, patternSize }, Occurrence::DecodeType::Binary, std::endian::native });
|
||||||
@ -397,7 +341,7 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
bool match = true;
|
bool match = true;
|
||||||
for (u32 i = 0; i < patternSize; i++) {
|
for (u32 i = 0; i < patternSize; i++) {
|
||||||
if ((data[i] & settings.pattern[i].mask) != settings.pattern[i].value) {
|
if (settings.pattern.matchesByte(data[i], i)) {
|
||||||
match = false;
|
match = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -693,8 +637,8 @@ namespace hex::plugin::builtin {
|
|||||||
constexpr static u32 min = 1, max = 0x1000;
|
constexpr static u32 min = 1, max = 0x1000;
|
||||||
ImGui::SliderScalar("hex.builtin.view.find.binary_pattern.alignment"_lang, ImGuiDataType_U32, &settings.alignment, &min, &max);
|
ImGui::SliderScalar("hex.builtin.view.find.binary_pattern.alignment"_lang, ImGuiDataType_U32, &settings.alignment, &min, &max);
|
||||||
|
|
||||||
settings.pattern = parseBinaryPatternString(settings.input);
|
settings.pattern = hex::BinaryPattern(settings.input);
|
||||||
this->m_settingsValid = !settings.pattern.empty() && settings.alignment > 0;
|
this->m_settingsValid = settings.pattern.isValid() && settings.alignment > 0;
|
||||||
|
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <hex/helpers/utils.hpp>
|
#include <hex/helpers/utils.hpp>
|
||||||
#include <hex/api/project_file_manager.hpp>
|
#include <hex/api/project_file_manager.hpp>
|
||||||
#include <hex/helpers/magic.hpp>
|
#include <hex/helpers/magic.hpp>
|
||||||
|
#include <hex/helpers/binary_pattern.hpp>
|
||||||
|
|
||||||
#include <content/popups/popup_file_chooser.hpp>
|
#include <content/popups/popup_file_chooser.hpp>
|
||||||
#include <content/popups/popup_question.hpp>
|
#include <content/popups/popup_question.hpp>
|
||||||
@ -765,6 +766,64 @@ namespace hex::plugin::builtin {
|
|||||||
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Format: [ AA BB CC DD ] @ 0x12345678
|
||||||
|
runtime.addPragma("magic", [provider, &foundCorrectType](pl::PatternLanguage &, const std::string &value) -> bool {
|
||||||
|
const auto pattern = [value = value] mutable -> std::optional<BinaryPattern> {
|
||||||
|
value = wolv::util::trim(value);
|
||||||
|
|
||||||
|
if (value.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (!value.starts_with('['))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
value = value.substr(1);
|
||||||
|
|
||||||
|
auto end = value.find(']');
|
||||||
|
if (end == std::string::npos)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
value = value.substr(0, end - 1);
|
||||||
|
value = wolv::util::trim(value);
|
||||||
|
|
||||||
|
return BinaryPattern(value);
|
||||||
|
}();
|
||||||
|
|
||||||
|
const auto address = [value = value] mutable -> std::optional<u64> {
|
||||||
|
value = wolv::util::trim(value);
|
||||||
|
|
||||||
|
if (value.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto start = value.find('@');
|
||||||
|
if (start == std::string::npos)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
value = value.substr(start + 1);
|
||||||
|
value = wolv::util::trim(value);
|
||||||
|
|
||||||
|
size_t end = 0;
|
||||||
|
auto result = std::stoull(value, &end, 0);
|
||||||
|
if (end != value.length())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (!address)
|
||||||
|
return false;
|
||||||
|
if (!pattern)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<u8> bytes(pattern->getSize());
|
||||||
|
provider->read(*address, bytes.data(), bytes.size());
|
||||||
|
|
||||||
|
if (pattern->matches(bytes))
|
||||||
|
foundCorrectType = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
this->m_possiblePatternFiles.clear();
|
this->m_possiblePatternFiles.clear();
|
||||||
|
|
||||||
std::error_code errorCode;
|
std::error_code errorCode;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user