diff --git a/README.md b/README.md
index 272aa1a..9b13197 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,8 @@
A multi-purpose adblocker and skip-bypass for the Spotify for Windows (64 bit)
Please support Spotify by purchasing premium
- Last updated: 14 February 2024
- Last tested version: Spotify for Windows (64 bit) 1.2.31.1205.g4d59ad7c
+ Last updated: 18 March 2024
+ Last tested version: Spotify for Windows (64 bit) 1.2.33.1039.g8ddb5918
@@ -61,6 +61,17 @@ rm -fo $env:APPDATA\spotify\dpapi.dll
rm -fo $env:APPDATA\spotify\config.ini
```
+### Disabling Automatic Updates
+
+The automatic update feature is enabled by default. To disable it:
+
+1. Navigate to the directory where Spotify is installed: `%APPDATA%\Spotify`.
+2. Open the `config.ini` file.
+3. Set `Enable_Auto_Update` to `0` under the `[Config]` section.
+4. Save your changes and close the file.
+
+Automatic updates will now be disabled. If you wish to update, you'll need to do so manually.
+
### Additional Notes:
* Installation script automatically detects if your Spotify client version is supported, or not. If the version is not supported, you will be prompted to update your Spotify client. To enforce client update, supply an optional parameter `UpdateSpotify` to the installation script.
diff --git a/blockthespot_settings.json b/blockthespot_settings.json
new file mode 100644
index 0000000..4a98b5f
--- /dev/null
+++ b/blockthespot_settings.json
@@ -0,0 +1,96 @@
+{
+ "Cef Offsets": {
+ "x64": {
+ "cef_request_t_get_url": 48,
+ "cef_zip_reader_t_get_file_name": 72,
+ "cef_zip_reader_t_read_file": 112
+ },
+ "x32": {
+ "cef_request_t_get_url": 24,
+ "cef_zip_reader_t_get_file_name": 36,
+ "cef_zip_reader_t_read_file": 56
+ }
+ },
+ "Developer": {
+ "x64": {
+ "Signature": "80 E3 01 48 8B 95 ?? ?? ?? ?? 48 83 FA 10",
+ "Value": "B3 01 90",
+ "Offset": 0,
+ "Address": 0
+ },
+ "x32": {
+ "Signature": "25 01 FF FF FF 89 ?? ?? ?? FF FF",
+ "Value": "B8 03 00",
+ "Offset": 0,
+ "Address": 0
+ }
+ },
+ "Zip Reader": {
+ "home-hpto.css": {
+ "hptocss": {
+ "Signature": ".WiPggcPDzbwGxoxwLWFf{display:-webkit-box;display:-ms-flexbox;display:flex;",
+ "Value": ".WiPggcPDzbwGxoxwLWFf{display:-webkit-box;display:-ms-flexbox;display:none;",
+ "Offset": 0,
+ "Fill": 0,
+ "Address": 0
+ }
+ },
+ "xpui.js": {
+ "adsEnabled": {
+ "Signature": "adsEnabled:!0",
+ "Value": "1",
+ "Offset": 12,
+ "Fill": 0,
+ "Address": 0
+ },
+ "ishptohidden": {
+ "Signature": "isHptoHidden:!0",
+ "Value": "1",
+ "Offset": 14,
+ "Fill": 0,
+ "Address": 0
+ },
+ "sponsorship": {
+ "Signature": ".set(\"allSponsorships\",t.sponsorships)}}(e,t);",
+ "Value": "\"",
+ "Offset": 5,
+ "Fill": 15,
+ "Address": 0
+ },
+ "skipsentry": {
+ "Signature": "sentry.io",
+ "Value": "localhost",
+ "Offset": 0,
+ "Fill": 0,
+ "Address": 0
+ },
+ "hptoEnabled": {
+ "Signature": "hptoEnabled:!0",
+ "Value": "1",
+ "Offset": 13,
+ "Fill": 0,
+ "Address": 0
+ },
+ "sp_localhost": {
+ "Signature": "sp://ads/v1/ads/",
+ "Value": "sp://localhost//",
+ "Offset": 0,
+ "Fill": 0,
+ "Address": 0
+ },
+ "premium_free": {
+ "Signature": "e.session?.productState?.catalogue?.toLowerCase()",
+ "Value": "\"\"",
+ "Offset": -1,
+ "Fill": 48,
+ "Address": 0
+ }
+ }
+ },
+ "Block List": [
+ "/ads/",
+ "/ad-logic/",
+ "/gabo-receiver-service/"
+ ],
+ "Latest Release Date": ""
+}
\ No newline at end of file
diff --git a/config.ini b/config.ini
index 51381a4..97d2803 100644
--- a/config.ini
+++ b/config.ini
@@ -2,5 +2,6 @@
Block_Ads=1
Block_Banner=1
Enable_Developer=1
+Enable_Auto_Update=1
;Log system
Enable_Log=0
diff --git a/install.ps1 b/install.ps1
index d60ead2..ac13ebe 100644
--- a/install.ps1
+++ b/install.ps1
@@ -265,6 +265,31 @@ $patchFiles = (Join-Path -Path $PWD -ChildPath 'dpapi.dll'), (Join-Path -Path $P
Copy-Item -LiteralPath $patchFiles -Destination "$spotifyDirectory"
+function Install-VcRedist {
+ # https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170
+ $vcRedistX86Url = "https://aka.ms/vs/17/release/vc_redist.x86.exe"
+ $vcRedistX64Url = "https://aka.ms/vs/17/release/vc_redist.x64.exe"
+
+ if ([Environment]::Is64BitOperatingSystem) {
+ if (!(Test-Path 'HKLM:\Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x64')) {
+ $vcRedistX64File = Join-Path -Path $PWD -ChildPath 'vc_redist.x64.exe'
+ Write-Host "Downloading and installing vc_redist.x64.exe..."
+ Get-File -Uri $vcRedistX64Url -TargetFile $vcRedistX64File
+ Start-Process -FilePath $vcRedistX64File -ArgumentList "/install /quiet /norestart" -Wait
+ }
+ }
+ else {
+ if (!(Test-Path 'HKLM:\Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x86')) {
+ $vcRedistX86File = Join-Path -Path $PWD -ChildPath 'vc_redist.x86.exe'
+ Write-Host "Downloading and installing vc_redist.x86.exe..."
+ Get-File -Uri $vcRedistX86Url -TargetFile $vcRedistX86File
+ Start-Process -FilePath $vcRedistX86File -ArgumentList "/install /quiet /norestart" -Wait
+ }
+ }
+}
+
+Install-VcRedist
+
$tempDirectory = $PWD
Pop-Location
diff --git a/src/BasicUtils/Json.cpp b/src/BasicUtils/Json.cpp
new file mode 100644
index 0000000..bfa77da
--- /dev/null
+++ b/src/BasicUtils/Json.cpp
@@ -0,0 +1,551 @@
+#include "Json.h"
+#include "Utils.h"
+#include "Logger.h"
+#include
+#include
+#include
+
+Json& Json::operator[](const std::wstring& key)
+{
+ if (auto* object_ptr = std::get_if(&m_value)) {
+ auto it = object_ptr->find(key);
+ if (it != object_ptr->end()) {
+ return it->second;
+ }
+ return (*object_ptr)[key];
+ }
+
+ m_value = Object();
+ return std::get(m_value)[key];
+}
+
+Json& Json::operator[](size_t index)
+{
+ return at(index);
+}
+
+Json& Json::operator=(std::initializer_list> list)
+{
+ m_value = Object(list.begin(), list.end());
+ return *this;
+}
+
+Json& Json::operator=(std::initializer_list list)
+{
+ m_value = Array(list.begin(), list.end());
+ return *this;
+}
+
+std::wostream& operator<<(std::wostream& os, const Json& json)
+{
+ std::wostringstream oss;
+ json.dump_impl(oss, 0, 0);
+ os << oss.str();
+ return os;
+}
+
+std::wistream& operator>>(std::wistream& is, Json& json)
+{
+ json = Json::parse(is);
+ return is;
+}
+
+bool operator==(const Json& lhs, const Json& rhs)
+{
+ return lhs.m_value == rhs.m_value;
+}
+
+bool operator!=(const Json& lhs, const Json& rhs)
+{
+ return !(lhs == rhs);
+}
+
+Json::Object::iterator Json::begin()
+{
+ if (is_object()) {
+ return std::get(m_value).begin();
+ }
+ throw std::runtime_error("Json value is not an object");
+}
+
+Json::Object::const_iterator Json::begin() const
+{
+ return const_cast(this)->begin();
+}
+
+Json::Object::iterator Json::end()
+{
+ if (is_object()) {
+ return std::get(m_value).end();
+ }
+ throw std::runtime_error("Json value is not an object");
+}
+
+Json::Object::const_iterator Json::end() const
+{
+ return const_cast(this)->end();
+}
+
+Json::Object::iterator Json::find(const std::wstring& key)
+{
+ if (is_object()) {
+ return std::get(m_value).find(key);
+ }
+ throw std::runtime_error("Json value is not an object");
+}
+
+Json::Object::const_iterator Json::find(const std::wstring& key) const
+{
+ return const_cast(this)->find(key);
+}
+
+//Json::Array::iterator Json::begin()
+//{
+// if (is_array()) {
+// return std::get(m_value).begin();
+// }
+// throw std::runtime_error("Json value is not an array");
+//}
+//
+//Json::Array::iterator Json::end()
+//{
+// if (is_array()) {
+// return std::get(m_value).end();
+// }
+// throw std::runtime_error("Json value is not an array");
+//}
+
+bool Json::is_null() const
+{
+ return std::holds_alternative(m_value);
+}
+
+bool Json::is_integer() const
+{
+ return std::holds_alternative(m_value);
+}
+
+bool Json::is_float() const
+{
+ return std::holds_alternative(m_value);
+}
+
+bool Json::is_double() const
+{
+ return std::holds_alternative(m_value);
+}
+
+bool Json::is_boolean() const
+{
+ return std::holds_alternative(m_value);
+}
+
+bool Json::is_string() const
+{
+ return std::holds_alternative(m_value);
+}
+
+bool Json::is_object() const
+{
+ return std::holds_alternative(m_value);
+}
+
+bool Json::is_array() const
+{
+ return std::holds_alternative(m_value);
+}
+
+int Json::get_integer() const
+{
+ if (std::holds_alternative(m_value)) {
+ return std::get(m_value);
+ }
+ Log(L"JSON value is not an integer", LogLevel::Error);
+ return 0;
+}
+
+float Json::get_float() const
+{
+ if (std::holds_alternative(m_value)) {
+ return std::get(m_value);
+ }
+ Log(L"JSON value is not an float", LogLevel::Error);
+ return 0.0f;
+}
+
+double Json::get_double() const
+{
+ if (std::holds_alternative(m_value)) {
+ return std::get(m_value);
+ }
+ Log(L"JSON value is not a double", LogLevel::Error);
+ return 0.0;
+}
+
+bool Json::get_boolean() const
+{
+ if (std::holds_alternative(m_value)) {
+ return std::get(m_value);
+ }
+ Log(L"JSON value is not a boolean", LogLevel::Error);
+ return false;
+}
+
+std::wstring Json::get_string() const
+{
+ if (std::holds_alternative(m_value)) {
+ return std::get(m_value);
+ }
+ Log(L"JSON value is not a string", LogLevel::Error);
+ return L"";
+}
+
+Json::Object Json::get_object() const
+{
+ if (std::holds_alternative(m_value)) {
+ return std::get(m_value);
+ }
+ Log(L"JSON value is not an object", LogLevel::Error);
+ return Object();
+}
+
+Json::Array Json::get_array() const
+{
+ if (std::holds_alternative(m_value)) {
+ return std::get(m_value);
+ }
+ Log(L"JSON value is not an array", LogLevel::Error);
+ return Array();
+}
+
+Json& Json::at(const std::wstring& key)
+{
+ if (auto* object_ptr = std::get_if(&m_value)) {
+ auto it = object_ptr->find(key);
+ if (it != object_ptr->end()) {
+ return it->second;
+ }
+ throw std::out_of_range("Key not found in JSON object");
+ }
+ throw std::runtime_error("Trying to access key in non-object JSON value");
+}
+
+const Json& Json::at(const std::wstring& key) const
+{
+ return const_cast(*this).at(key);
+}
+
+Json& Json::at(std::size_t index)
+{
+ if (auto* array_ptr = std::get_if(&m_value)) {
+ if (index < array_ptr->size()) {
+ return (*array_ptr)[index];
+ }
+ throw std::out_of_range("Index out of range in JSON array");
+ }
+ throw std::out_of_range("Trying to access index in non-array JSON value");
+}
+
+const Json& Json::at(std::size_t index) const
+{
+ return const_cast(*this).at(index);
+}
+
+void Json::clear()
+{
+ if (std::holds_alternative(m_value)) {
+ std::get(m_value).clear();
+ }
+ else if (std::holds_alternative(m_value)) {
+ std::get(m_value).clear();
+ }
+}
+
+bool Json::empty() const
+{
+ if (std::holds_alternative(m_value)) {
+ return std::get(m_value).empty();
+ }
+ else if (std::holds_alternative(m_value)) {
+ return std::get(m_value).empty();
+ }
+ else if (std::holds_alternative(m_value)) {
+ return true;
+ }
+ return false;
+}
+
+std::size_t Json::size() const
+{
+ if (std::holds_alternative(m_value)) {
+ return std::get(m_value).size();
+ }
+ else if (std::holds_alternative(m_value)) {
+ return std::get(m_value).size();
+ }
+ return 0;
+}
+
+bool Json::contains(const std::wstring& key) const
+{
+ if (is_object()) {
+ const auto& object = std::get(m_value);
+ return object.find(key) != object.end();
+ }
+ return false;
+}
+
+std::wstring Json::dump(int indent) const
+{
+ std::wostringstream os;
+ dump_impl(os, indent, indent);
+ return os.str();
+}
+
+void Json::dump_impl(std::wostream& os, int indent, int step) const
+{
+ std::visit([&os, indent, step](const auto& value) {
+ if constexpr (std::is_same_v, Object>) {
+ os << (indent == 0 ? L"{" : L"{\n");
+ bool first = true;
+ for (const auto& [key, val] : value) {
+ if (!first) {
+ os << (indent == 0 ? L", " : L",\n");
+ }
+ first = false;
+ os << std::wstring(indent, L' ') << L"\"" << key << L"\": ";
+ val.dump_impl(os, indent + step, step);
+ }
+ os << (indent == 0 ? L"}" : L"\n" + std::wstring(indent - step, L' ') + L"}");
+ }
+ else if constexpr (std::is_same_v, Array>) {
+ os << (indent == 0 ? L"[" : L"[\n");
+ bool first = true;
+ for (const auto& val : value) {
+ if (!first) {
+ os << (indent == 0 ? L", " : L",\n");
+ }
+ first = false;
+ if (indent != 0)
+ os << std::wstring(indent, L' ');
+ val.dump_impl(os, indent + step, step);
+ }
+ os << (indent == 0 ? L"]" : L"\n" + std::wstring(indent - step, L' ') + L"]");
+ }
+ else {
+ if constexpr (std::is_same_v, std::wstring>)
+ os << std::quoted(value);
+ else if constexpr (std::is_same_v, bool>)
+ os << (value ? L"true" : L"false");
+ else if constexpr (std::is_same_v, std::nullptr_t>)
+ os << L"null";
+ else
+ os << value;
+ }
+ }, m_value);
+}
+
+Json Json::parse(const std::wstring& json_text)
+{
+ std::wistringstream iss(json_text);
+ return parse(iss);
+}
+
+Json Json::parse(std::wistream& is)
+{
+ try {
+ wchar_t ch;
+ is >> ch;
+ is.unget();
+
+ if (ch == L'{') {
+ return parse_object(is);
+ }
+ else if (ch == L'[') {
+ return parse_array(is);
+ }
+ else if (ch == L'"') {
+ return parse_string(is);
+ }
+ else if (ch == L'-' || std::iswdigit(ch)) {
+ return parse_number(is);
+ }
+ else if (ch == L't' || ch == L'f') {
+ return parse_boolean(is);
+ }
+ else if (ch == L'n') {
+ return parse_null(is);
+ }
+ else {
+ throw std::runtime_error("Invalid JSON");
+ }
+ }
+ catch (const std::exception& e) {
+ Log(Utils::FormatString(L"{}", e.what()), LogLevel::Error);
+ return Json();
+ }
+}
+
+Json::Object Json::parse_object(std::wistream& is)
+{
+ Object result;
+ wchar_t ch;
+ is >> ch;
+ while (is >> ch) {
+ if (ch == L'}') {
+ return result;
+ }
+ is.unget();
+ std::wstring key = parse_string(is);
+ is >> ch;
+ if (ch != L':') {
+ throw std::runtime_error("Expected ':' in JSON object");
+ }
+ Json value = parse(is);
+ result[key] = value;
+ is >> ch;
+ if (ch == L'}') {
+ return result;
+ }
+ else if (ch != L',') {
+ throw std::runtime_error("Expected ',' or '}' in JSON object");
+ }
+ }
+ throw std::runtime_error("Unexpected end of JSON object");
+}
+
+Json::Array Json::parse_array(std::wistream& is)
+{
+ Array result;
+ wchar_t ch;
+ is >> ch;
+ while (is >> ch) {
+ if (ch == L']') {
+ return result;
+ }
+ is.unget();
+ Json value = parse(is);
+ result.push_back(value);
+ is >> ch;
+ if (ch == L']') {
+ return result;
+ }
+ else if (ch != L',') {
+ throw std::runtime_error("Expected ',' or ']' in JSON array");
+ }
+ }
+ throw std::runtime_error("Unexpected end of JSON array");
+}
+
+std::wstring Json::parse_string(std::wistream& is)
+{
+ std::wstring result;
+ wchar_t ch;
+ is >> std::ws;
+ if (!(is >> ch) || ch != L'"') {
+ throw std::runtime_error("Expected opening quote in JSON string");
+ }
+
+ while (is.get(ch)) {
+ if (ch == L'\\') {
+ if (!(is.get(ch))) {
+ throw std::runtime_error("Unexpected end of JSON string");
+ }
+ switch (ch) {
+ case L'"': result += L'"'; break;
+ case L'\\': result += L'\\'; break;
+ case L'/': result += L'/'; break;
+ case L'b': result += L'\b'; break;
+ case L'f': result += L'\f'; break;
+ case L'n': result += L'\n'; break;
+ case L'r': result += L'\r'; break;
+ case L't': result += L'\t'; break;
+ case L'u': {
+ std::wstring hex_code;
+ for (int i = 0; i < 4; ++i) {
+ if (!(is.get(ch))) {
+ throw std::runtime_error("Incomplete Unicode escape sequence in JSON string");
+ }
+ hex_code += ch;
+ }
+ wchar_t unicode_char = static_cast(std::stoi(hex_code, nullptr, 16));
+ result += unicode_char;
+ break;
+ }
+ default: throw std::runtime_error("Invalid JSON string escape sequence");
+ }
+ }
+ else if (ch == L'"') {
+ break;
+ }
+ else {
+ result += ch;
+ }
+ }
+
+ if (ch != L'"') {
+ throw std::runtime_error("Unexpected end of JSON string");
+ }
+
+ return result;
+}
+
+Json Json::parse_number(std::wistream& is)
+{
+ wchar_t ch;
+ std::wstring number_str;
+ while (is >> ch && (std::iswdigit(ch) || ch == L'.' || ch == L'e' || ch == L'E' || ch == L'+' || ch == L'-')) {
+ number_str += ch;
+ }
+ is.unget();
+ std::wistringstream number_iss(number_str);
+ double number;
+ number_iss >> number;
+ if (number_iss.fail() || !number_iss.eof()) {
+ throw std::runtime_error("Invalid JSON number");
+ }
+
+ if (number_str.find(L'.') != std::wstring::npos || number_str.find(L'e') != std::wstring::npos || number_str.find(L'E') != std::wstring::npos) {
+ return number;
+ }
+ else {
+ return static_cast(number);
+ }
+}
+
+Json Json::parse_boolean(std::wistream& is)
+{
+ std::wstring boolean_str;
+ wchar_t ch;
+ for (int i = 0; i < 5; ++i) {
+ if (!(is.get(ch))) {
+ throw std::runtime_error("Unexpected end of JSON boolean");
+ }
+ boolean_str += ch;
+ if (boolean_str == L"true") {
+ return true;
+ }
+ else if (boolean_str == L"false") {
+ return false;
+ }
+ }
+ throw std::runtime_error("Invalid JSON boolean");
+}
+
+Json Json::parse_null(std::wistream& is)
+{
+ std::wstring null_str;
+ wchar_t ch;
+ for (int i = 0; i < 4; ++i) {
+ if (!(is.get(ch))) {
+ throw std::runtime_error("Unexpected end of JSON null");
+ }
+ null_str += ch;
+ }
+ if (null_str == L"null") {
+ return Json();
+ }
+ else {
+ throw std::runtime_error("Invalid JSON null");
+ }
+}
diff --git a/src/BasicUtils/Json.h b/src/BasicUtils/Json.h
new file mode 100644
index 0000000..9a06d4e
--- /dev/null
+++ b/src/BasicUtils/Json.h
@@ -0,0 +1,150 @@
+#ifndef JSON_H
+#define JSON_H
+
+#include
+#include
+#include
+#include
+#include
+
+template class Template>
+struct is_specialization : std::false_type {};
+
+template class Template, typename... Args>
+struct is_specialization, Template> : std::true_type {};
+
+template
+inline constexpr bool is_map_v = is_specialization, std::unordered_map>::value;
+
+template
+inline constexpr bool is_vector_v = is_specialization, std::vector>::value;
+
+class Json {
+public:
+ using Object = std::unordered_map;
+ using Array = std::vector;
+ using Value = std::variant;
+
+ template
+ requires std::is_constructible_v
+ Json(T&& value) : m_value(std::forward(value)) {}
+
+ template
+ requires std::is_constructible_v
+ Json(const std::vector& list) : m_value(Array(list.begin(), list.end())) {}
+
+ template
+ requires std::is_constructible_v
+ Json(const std::initializer_list& list) : m_value(Array(list.begin(), list.end())) {}
+
+ Json() : m_value(nullptr) {}
+ Json(std::initializer_list> list) : m_value(Object(list.begin(), list.end())) {}
+
+ Json& operator[](const std::wstring& key);
+ Json& operator[](size_t index);
+
+ Json& operator=(std::initializer_list> list);
+ Json& operator=(std::initializer_list list);
+
+ friend std::wostream& operator<<(std::wostream& os, const Json& json);
+ friend std::wistream& operator>>(std::wistream& is, Json& json);
+ friend bool operator==(const Json& lhs, const Json& rhs);
+ friend bool operator!=(const Json& lhs, const Json& rhs);
+
+ Object::iterator begin();
+ Object::const_iterator begin() const;
+
+ Object::iterator end();
+ Object::const_iterator end() const;
+
+ Object::iterator find(const std::wstring& key);
+ Object::const_iterator find(const std::wstring& key) const;
+
+ bool is_null() const;
+ bool is_integer() const;
+ bool is_float() const;
+ bool is_double() const;
+ bool is_boolean() const;
+ bool is_string() const;
+ bool is_object() const;
+ bool is_array() const;
+
+ int get_integer() const;
+ float get_float() const;
+ double get_double() const;
+ bool get_boolean() const;
+ std::wstring get_string() const;
+ Object get_object() const;
+ Array get_array() const;
+
+ template
+ void get_to(T& value) const
+ {
+ if constexpr (std::is_same_v) {
+ value = *this;
+ }
+ else if constexpr (std::is_constructible_v) {
+ if (!std::holds_alternative(m_value)) {
+ throw std::runtime_error("JSON value cannot be converted to the requested type");
+ }
+ value = std::get(m_value);
+ }
+ else if constexpr (is_vector_v) {
+ if (!std::holds_alternative(m_value)) {
+ throw std::runtime_error("JSON value is not an array");
+ }
+ const Array& array = std::get(m_value);
+ value.clear();
+ for (const Json& element : array) {
+ typename T::value_type element_value;
+ element.get_to(element_value);
+ value.push_back(element_value);
+ }
+ }
+ else if constexpr (is_map_v) {
+ if (!std::holds_alternative(m_value)) {
+ throw std::runtime_error("JSON value is not an object");
+ }
+ const Object& object = std::get(m_value);
+ value.clear();
+ for (const auto& [key, element] : object) {
+ typename T::mapped_type element_value;
+ element.get_to(element_value);
+ value[key] = element_value;
+ }
+ }
+ else {
+ throw std::runtime_error("Unsupported type for conversion from JSON");
+ }
+ }
+
+ Json& at(const std::wstring& key);
+ const Json& at(const std::wstring& key) const;
+
+ Json& at(size_t index);
+ const Json& at(size_t index) const;
+
+ void clear();
+ bool empty() const;
+ size_t size() const;
+
+ bool contains(const std::wstring& key) const;
+
+ std::wstring dump(int indent = 0) const;
+
+ static Json parse(const std::wstring& json_text);
+ static Json parse(std::wistream& is);
+
+private:
+ Value m_value;
+
+ void dump_impl(std::wostream& os, int indent, int step) const;
+ static Object parse_object(std::wistream& is);
+ static Array parse_array(std::wistream& is);
+ static std::wstring parse_string(std::wistream& is);
+ static Json parse_number(std::wistream& is);
+ static Json parse_boolean(std::wistream& is);
+ static Json parse_null(std::wistream& is);
+};
+
+#endif // JSON_H
\ No newline at end of file
diff --git a/src/BasicUtils/Logger.cpp b/src/BasicUtils/Logger.cpp
index 868a68e..03cb6bf 100644
--- a/src/BasicUtils/Logger.cpp
+++ b/src/BasicUtils/Logger.cpp
@@ -7,11 +7,12 @@
#include
namespace Logger {
- std::mutex mtx;
std::wofstream file;
bool log_enabled = false;
+ bool has_error = false;
- void Init(std::wstring_view log_file, bool enable_logging) {
+ void Init(std::wstring_view log_file, bool enable_logging)
+ {
log_enabled = enable_logging;
if (log_enabled) {
file.open(log_file.data(), std::ios::out | std::ios::trunc);
@@ -21,7 +22,8 @@ namespace Logger {
}
}
- std::wstring GetLevelInfo(LogLevel level) {
+ std::wstring GetLevelInfo(LogLevel level)
+ {
switch (level) {
case LogLevel::Info:
return L"INFO";
@@ -32,14 +34,16 @@ namespace Logger {
}
}
- void Log(std::wstring_view message, LogLevel level) {
-#ifndef NDEBUG
+ void Log(std::wstring_view message, LogLevel level)
+ {
if (level == LogLevel::Error) {
+ has_error = true;
PrintError(L"{}", message);
}
-#endif
+
if (!log_enabled) return;
+ static std::mutex mtx;
std::lock_guard lock(mtx);
if (file.is_open()) {
@@ -50,4 +54,9 @@ namespace Logger {
file.flush();
}
}
+
+ bool HasError()
+ {
+ return has_error;
+ }
}
diff --git a/src/BasicUtils/Logger.h b/src/BasicUtils/Logger.h
index 22df397..7c5722f 100644
--- a/src/BasicUtils/Logger.h
+++ b/src/BasicUtils/Logger.h
@@ -8,6 +8,7 @@ namespace Logger
enum class LogLevel { Info, Error };
void Init(std::wstring_view file, bool enable);
void Log(std::wstring_view message, LogLevel level);
+ bool HasError();
}
using Logger::LogLevel;
diff --git a/src/BasicUtils/Memory.cpp b/src/BasicUtils/Memory.cpp
index 23ee697..328b902 100644
--- a/src/BasicUtils/Memory.cpp
+++ b/src/BasicUtils/Memory.cpp
@@ -24,13 +24,23 @@ namespace Memory {
return false;
}
- bool Write(void* address, std::string_view& data)
+ bool Write(void* address, const std::string_view& data)
+ {
+ return Write(address, data.data(), data.size());
+ }
+
+ bool Write(void* address, const std::wstring_view& data)
{
return Write(address, data.data(), data.size());
}
- bool Write(void* address, std::initializer_list& data)
+ bool Write(void* address, const std::initializer_list& data)
{
return Write(address, data.begin(), data.size());
}
+
+ bool Write(void* address, const std::vector& data)
+ {
+ return Write(address, data.data(), data.size());
+ }
}
\ No newline at end of file
diff --git a/src/BasicUtils/Memory.h b/src/BasicUtils/Memory.h
index b432e50..46ae1ff 100644
--- a/src/BasicUtils/Memory.h
+++ b/src/BasicUtils/Memory.h
@@ -3,13 +3,16 @@
#include
#include
+#include
namespace Memory {
bool Read(void* address, void* buffer, size_t size);
bool Write(void* address, const void* data, size_t size);
- bool Write(void* address, std::string_view& data);
- bool Write(void* address, std::initializer_list& data);
+ bool Write(void* address, const std::string_view& data);
+ bool Write(void* address, const std::wstring_view& data);
+ bool Write(void* address, const std::initializer_list& data);
+ bool Write(void* address, const std::vector& data);
}
diff --git a/src/BasicUtils/MemoryScanner.cpp b/src/BasicUtils/MemoryScanner.cpp
index f5112d4..4247d30 100644
--- a/src/BasicUtils/MemoryScanner.cpp
+++ b/src/BasicUtils/MemoryScanner.cpp
@@ -1,296 +1,385 @@
-#include "MemoryScanner.h"
-#include "Hooking.h"
-#include "Utils.h"
-#include "Memory.h"
-#include
-#include
-#include
-#include
-#include
-
-namespace MemoryScanner
-{
- ModuleInfo GetModuleInfo(std::wstring_view module_name)
- {
- static std::unordered_map loaded_modules;
-
- const auto module = loaded_modules.find(module_name);
- if (module != loaded_modules.end()) {
- return module->second;
- }
-
- HMODULE module_handle = GetModuleHandleW(module_name.empty() ? nullptr : module_name.data());
- if (module_handle == nullptr) {
- return ModuleInfo(module_name, 0, 0);
- }
-
- MODULEINFO module_info;
- if (!GetModuleInformation(GetCurrentProcess(), module_handle, &module_info, sizeof(MODULEINFO))) {
- return ModuleInfo(module_name, 0, 0);
- }
-
- const auto ret = ModuleInfo(module_name, reinterpret_cast(module_info.lpBaseOfDll), module_info.SizeOfImage);
- loaded_modules.emplace(module_name, ret);
- return ret;
- }
-
- ScanResult GetFunctionAddress(std::string_view module_name, std::string_view function_name)
- {
- HMODULE module_handle = GetModuleHandleA(module_name.data());
- if (module_handle == nullptr) {
- module_handle = LoadLibraryA(module_name.data());
- if (module_handle == nullptr) {
- return ScanResult(0, 0, 0);
- }
- }
-
- FARPROC function_address = GetProcAddress(module_handle, function_name.data());
- if (function_address == nullptr) {
- return ScanResult(0, 0, 0);
- }
-
- MODULEINFO module_info;
- if (!GetModuleInformation(GetCurrentProcess(), module_handle, &module_info, sizeof(MODULEINFO))) {
- return ScanResult(reinterpret_cast(function_address), 0, 0);
- }
-
- return ScanResult(reinterpret_cast(function_address), reinterpret_cast(module_info.lpBaseOfDll), module_info.SizeOfImage);
- }
-
- std::vector SignatureToByteArray(std::wstring_view signature)
- {
- std::vector signature_bytes;
- std::wstring word;
- std::wistringstream iss(signature.data());
-
- while (iss >> word) {
- if (word.size() == 1 && word[0] == L'?') {
- signature_bytes.push_back(0);
- }
- else if (word.size() == 2 && word[0] == L'?' && word[1] == L'?') {
- signature_bytes.push_back(0);
- }
- else if (word.size() == 2 && std::isxdigit(word[0]) && std::isxdigit(word[1])) {
- unsigned long value = std::stoul(word, nullptr, 16);
- if (value > 255) {
- return { 0 };
- }
- signature_bytes.push_back(static_cast(value));
- }
- else {
- for (wchar_t c : word) {
- if (c > 255) {
- return { 0 };
- }
- signature_bytes.push_back(static_cast(c));
- }
- }
- }
-
- return signature_bytes;
- }
-
- std::vector ScanAll(uintptr_t base_address, size_t image_size, const std::vector& pattern_byte, bool only_first)
- {
- std::vector matches;
-
- size_t pattern_size = pattern_byte.size();
-
- if (pattern_byte.empty()) {
- return matches;
- }
-
- uint8_t* base_ptr = reinterpret_cast(base_address);
- uintptr_t end_address = base_address + image_size - pattern_size + 1;
-
- while (base_ptr < reinterpret_cast(end_address)) {
- base_ptr = static_cast(memchr(base_ptr, pattern_byte[0], end_address - reinterpret_cast(base_ptr)));
- if (!base_ptr) {
- break;
- }
-
- bool found = true;
- for (size_t i = 1; i < pattern_size; ++i) {
- if (pattern_byte[i] != 0 && pattern_byte[i] != base_ptr[i]) {
- found = false;
- break;
- }
- }
-
- if (found) {
- matches.push_back(ScanResult(reinterpret_cast(base_ptr), base_address, image_size));
- if (only_first) break;
- }
-
- ++base_ptr;
- }
-
- return matches;
- }
-
- std::vector ScanAll(uintptr_t base_address, size_t image_size, std::wstring_view signature)
- {
- return ScanAll(base_address, image_size, SignatureToByteArray(signature));
- }
-
- std::vector ScanAll(std::wstring_view signature, std::wstring_view module_name)
- {
- const auto mod = GetModuleInfo(module_name);
- return ScanAll(mod.base_address, mod.module_size, signature);
- }
-
- ScanResult ScanFirst(uintptr_t base_address, size_t image_size, const std::vector& pattern_byte)
- {
- const auto matches = ScanAll(base_address, image_size, pattern_byte, true);
- return matches.empty() ? ScanResult(0, 0, 0) : matches.at(0);
- }
-
- ScanResult ScanFirst(uintptr_t base_address, size_t image_size, std::wstring_view signature)
- {
- return ScanFirst(base_address, image_size, SignatureToByteArray(signature));
- }
-
- ScanResult ScanFirst(std::wstring_view signature, std::wstring_view module_name)
- {
- const auto mod = GetModuleInfo(module_name);
- return ScanFirst(mod.base_address, mod.module_size, signature);
- }
-
- ScanResult::ScanResult(uintptr_t address, uintptr_t base, size_t size) : m_address(address), m_base_address(base), m_image_size(size)
- {
- //...
- }
-
- ScanResult::operator uintptr_t() const
- {
- return m_address;
- }
-
- bool ScanResult::is_valid(const std::vector& value) const
- {
- if (m_address == 0) {
- return false;
- }
-
- for (size_t i = 0; i < value.size(); ++i) {
- if (*(reinterpret_cast(m_address) + i) != value[i])
- return false;
- }
-
- return true;
- }
-
- uint8_t* ScanResult::data() const
- {
- if (!is_valid()) {
- return nullptr;
- }
-
- return reinterpret_cast(m_address);
- }
-
- ScanResult ScanResult::rva() const
- {
- if (!is_valid()) {
- return ScanResult(0, m_base_address, m_image_size);
- }
-
- uintptr_t rva_address = m_address - m_base_address;
- return ScanResult(rva_address, m_base_address, m_image_size);
- }
-
- ScanResult ScanResult::offset(std::ptrdiff_t offset_value) const
- {
- if (!is_valid()) {
- return ScanResult(0, m_base_address, m_image_size);
- }
-
- uintptr_t new_address = m_address;
- if (offset_value >= 0) {
- new_address += static_cast(offset_value);
- }
- else {
- new_address -= static_cast(-offset_value);
- }
-
- return ScanResult(new_address, m_base_address, m_image_size);
- }
-
- ScanResult ScanResult::scan_first(std::wstring_view value) const
- {
- return is_valid() ? ScanFirst(m_address, m_image_size - rva(), value) : ScanResult(0, m_base_address, m_image_size);
- }
-
- bool ScanResult::write(const void* data, size_t size) const
- {
- return Memory::Write(reinterpret_cast(m_address), data, size);
- }
-
- bool ScanResult::write(std::string_view data) const
- {
- return Memory::Write(reinterpret_cast(m_address), data);
- }
-
- bool ScanResult::write(std::initializer_list data) const
- {
- return Memory::Write(reinterpret_cast(m_address), data);
- }
-
- PVOID* ScanResult::hook(PVOID hook_function) const
- {
- return (is_valid() && Hooking::HookFunction(&(PVOID&)m_address, hook_function)) ? reinterpret_cast(m_address) : NULL;
- }
-
- bool ScanResult::unhook() const
- {
- return is_valid() ? Hooking::UnhookFunction(&(PVOID&)m_address) : false;
- }
-
- std::vector ScanResult::get_all_matching_codes(const std::vector& pattern_byte, bool calculate_relative_offset, uintptr_t base_address, size_t image_size, bool only_first) const
- {
- if (base_address == 0) base_address = m_base_address;
- if (image_size == 0) image_size = m_image_size;
-
- if (!calculate_relative_offset) {
- std::vector new_pattern_byte = pattern_byte;
- new_pattern_byte.insert(new_pattern_byte.end(), reinterpret_cast(&m_address),
- reinterpret_cast(&m_address) + sizeof(m_address));
-
- return ScanAll(base_address, image_size, new_pattern_byte, only_first);
- }
-
- std::vector matches;
- const auto all_matches = ScanAll(base_address, image_size, pattern_byte);
- for (const auto& address : all_matches) {
- const auto offset_address = address + pattern_byte.size();
- const auto relative_offset = static_cast(m_address) - static_cast(offset_address) - sizeof(int32_t);
- if (*reinterpret_cast(offset_address) == relative_offset) {
- matches.push_back(ScanResult(address, base_address, image_size));
- if (only_first) break;
- }
- }
-
- return matches;
- }
-
- ScanResult ScanResult::get_first_matching_code(const std::vector& pattern_byte, bool calculate_relative_offset, uintptr_t base_address, size_t image_size) const
- {
- const auto matches = get_all_matching_codes(pattern_byte, calculate_relative_offset, base_address, image_size, true);
- return matches.empty() ? ScanResult(0, m_base_address, m_image_size) : matches.at(0);
- }
-
- uintptr_t ScanResult::get_base_address() const
- {
- return m_base_address;
- }
-
- size_t ScanResult::get_image_size() const
- {
- return m_image_size;
- }
-
- void ScanResult::print_address() const
- {
- Print(L"{:x}", m_address);
- }
+#include "MemoryScanner.h"
+#include "Hooking.h"
+#include "Utils.h"
+#include "Memory.h"
+#include
+#include
+#include
+#include
+#include
+
+namespace MemoryScanner
+{
+ ModuleInfo GetModuleInfo(std::wstring_view module_name)
+ {
+ static std::unordered_map loaded_modules;
+
+ const auto module = loaded_modules.find(module_name);
+ if (module != loaded_modules.end()) {
+ return module->second;
+ }
+
+ HMODULE module_handle = GetModuleHandleW(module_name.empty() ? nullptr : module_name.data());
+ if (module_handle == nullptr) {
+ return ModuleInfo(module_name, 0, 0);
+ }
+
+ MODULEINFO module_info;
+ if (!GetModuleInformation(GetCurrentProcess(), module_handle, &module_info, sizeof(MODULEINFO))) {
+ return ModuleInfo(module_name, 0, 0);
+ }
+
+ const auto ret = ModuleInfo(module_name, reinterpret_cast(module_info.lpBaseOfDll), module_info.SizeOfImage);
+ loaded_modules.emplace(module_name, ret);
+ return ret;
+ }
+
+ ScanResult GetFunctionAddress(std::string_view module_name, std::string_view function_name)
+ {
+ HMODULE module_handle = GetModuleHandleA(module_name.data());
+ if (module_handle == nullptr) {
+ module_handle = LoadLibraryA(module_name.data());
+ if (module_handle == nullptr) {
+ return ScanResult(0, 0, 0);
+ }
+ }
+
+ FARPROC function_address = GetProcAddress(module_handle, function_name.data());
+ if (function_address == nullptr) {
+ return ScanResult(0, 0, 0);
+ }
+
+ MODULEINFO module_info;
+ if (!GetModuleInformation(GetCurrentProcess(), module_handle, &module_info, sizeof(MODULEINFO))) {
+ return ScanResult(reinterpret_cast(function_address), 0, 0);
+ }
+
+ return ScanResult(reinterpret_cast(function_address), reinterpret_cast(module_info.lpBaseOfDll), module_info.SizeOfImage);
+ }
+
+ std::vector ParseBytePattern(std::wstring_view byte_pattern)
+ {
+ std::vector parsed_pattern;
+ bool is_hex = byte_pattern.find_first_not_of(L"0123456789ABCDEFabcdef? ") == std::wstring::npos;
+ if (is_hex) {
+ std::wistringstream iss(byte_pattern.data());
+ std::wstring byte;
+ while (iss >> byte) {
+ BytePattern bp;
+ if (byte.size() == 1 && byte[0] == L'?') {
+ bp.half_byte[0].wildcard = true;
+ bp.half_byte[1].wildcard = true;
+ }
+ else {
+ if (byte[0] == L'?') {
+ bp.half_byte[0].wildcard = true;
+ }
+ else {
+ bp.half_byte[0].data = std::stoi(std::wstring(1, byte[0]), nullptr, 16);
+ }
+
+ if (byte[1] == L'?') {
+ bp.half_byte[1].wildcard = true;
+ }
+ else {
+ bp.half_byte[1].data = std::stoi(std::wstring(1, byte[1]), nullptr, 16);
+ }
+ }
+ parsed_pattern.push_back(bp);
+ }
+ }
+ else {
+ for (wchar_t ch : byte_pattern) {
+ BytePattern bp;
+ bp.half_byte[0].data = ch >> 4;
+ bp.half_byte[1].data = ch & 0xF;
+ parsed_pattern.push_back(bp);
+ }
+ }
+
+ return parsed_pattern;
+ }
+
+ std::vector ScanAll(uintptr_t base_address, size_t image_size, const std::vector& parsed_pattern, bool only_first)
+ {
+ std::vector result;
+ const uint8_t* start = reinterpret_cast(base_address);
+ const uint8_t* end = start + image_size;
+ const size_t pattern_size = parsed_pattern.size();
+ const BytePattern& first_pattern = parsed_pattern[0];
+
+ for (const uint8_t* it = start; it + pattern_size <= end; ++it) {
+ if ((first_pattern.half_byte[0].wildcard || ((it[0] & 0xF0) == (first_pattern.half_byte[0].data << 4))) &&
+ (first_pattern.half_byte[1].wildcard || ((it[0] & 0x0F) == first_pattern.half_byte[1].data))) {
+
+ bool found = true;
+ for (size_t i = 1; i < pattern_size; ++i) {
+ const BytePattern& pattern = parsed_pattern[i];
+ uint8_t byte = it[i];
+
+ if (!(pattern.half_byte[0].wildcard || ((byte & 0xF0) == (pattern.half_byte[0].data << 4))) ||
+ !(pattern.half_byte[1].wildcard || ((byte & 0x0F) == pattern.half_byte[1].data))) {
+ found = false;
+ break;
+ }
+ }
+
+ if (found) {
+ result.push_back(ScanResult(reinterpret_cast(it), base_address, image_size));
+ if (only_first) {
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ std::vector ScanAll(uintptr_t base_address, size_t image_size, std::wstring_view pattern)
+ {
+ return ScanAll(base_address, image_size, ParseBytePattern(pattern));
+ }
+
+ std::vector ScanAll(std::wstring_view pattern, std::wstring_view module_name)
+ {
+ const auto module_info = GetModuleInfo(module_name);
+ return ScanAll(module_info.base_address, module_info.module_size, pattern);
+ }
+
+ ScanResult ScanFirst(uintptr_t base_address, size_t image_size, const std::vector& parsed_pattern)
+ {
+ const auto addresses = ScanAll(base_address, image_size, parsed_pattern, true);
+ return addresses.empty() ? ScanResult(0, 0, 0) : addresses.front();
+ }
+
+ ScanResult ScanFirst(uintptr_t base_address, size_t image_size, std::wstring_view pattern)
+ {
+ return ScanFirst(base_address, image_size, ParseBytePattern(pattern));
+ }
+
+ ScanResult ScanFirst(std::wstring_view pattern, std::wstring_view module_name)
+ {
+ const auto module_info = GetModuleInfo(module_name);
+ return ScanFirst(module_info.base_address, module_info.module_size, pattern);
+ }
+
+ ScanResult::ScanResult(uintptr_t address, uintptr_t base, size_t size, bool is_rva) : m_address(address), m_base_address(base), m_image_size(size)
+ {
+ if (is_rva && address) {
+ m_address += m_base_address;
+ }
+ }
+
+ ScanResult::ScanResult(uintptr_t address, std::wstring_view module_name, bool is_rva) : m_address(address), m_base_address(GetModuleInfo(module_name).base_address), m_image_size(GetModuleInfo(module_name).module_size)
+ {
+ if (is_rva && address) {
+ m_address += m_base_address;
+ }
+ }
+
+ ScanResult::operator uintptr_t() const
+ {
+ return m_address;
+ }
+
+ bool ScanResult::is_valid(const std::vector& parsed_pattern) const
+ {
+ if (m_address == 0) {
+ return false;
+ }
+
+ for (size_t i = 0; i < parsed_pattern.size(); ++i) {
+ BytePattern pattern = parsed_pattern[i];
+ if (pattern.half_byte[0].wildcard && pattern.half_byte[1].wildcard) {
+ continue;
+ }
+
+ uint8_t byte = *(reinterpret_cast(m_address) + i);
+ uint8_t pattern_byte = (pattern.half_byte[0].data << 4) | pattern.half_byte[1].data;
+ bool match_high_nibble = pattern.half_byte[0].wildcard || (byte & 0xF0) == (pattern_byte & 0xF0);
+ bool match_low_nibble = pattern.half_byte[1].wildcard || (byte & 0x0F) == (pattern_byte & 0x0F);
+
+ if (!(match_high_nibble && match_low_nibble)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool ScanResult::is_valid(std::wstring_view pattern) const
+ {
+ return is_valid(ParseBytePattern(pattern));
+ }
+
+ uint8_t* ScanResult::data() const
+ {
+ if (!is_valid()) {
+ return nullptr;
+ }
+
+ return reinterpret_cast(m_address);
+ }
+
+ ScanResult ScanResult::rva() const
+ {
+ if (!is_valid()) {
+ return ScanResult(0, m_base_address, m_image_size);
+ }
+
+ return ScanResult(m_address - m_base_address, m_base_address, m_image_size);
+ }
+
+ ScanResult ScanResult::offset(std::ptrdiff_t offset_value) const
+ {
+ if (!is_valid()) {
+ return ScanResult(0, m_base_address, m_image_size);
+ }
+
+ uintptr_t new_address = m_address;
+ if (offset_value >= 0) {
+ new_address += static_cast(offset_value);
+ }
+ else {
+ new_address -= static_cast(-offset_value);
+ }
+
+ return ScanResult(new_address, m_base_address, m_image_size);
+ }
+
+ ScanResult ScanResult::scan_first(std::wstring_view value) const
+ {
+ return is_valid() ? ScanFirst(m_address, m_image_size - rva(), value) : ScanResult(0, m_base_address, m_image_size);
+ }
+
+ bool ScanResult::write(const std::string_view& data) const
+ {
+ return is_valid() ? Memory::Write(reinterpret_cast(m_address), data) : false;
+ }
+
+ bool ScanResult::write(const std::wstring_view& data) const
+ {
+ return is_valid() ? Memory::Write(reinterpret_cast(m_address), data) : false;
+ }
+
+ bool ScanResult::write(const std::initializer_list& data) const
+ {
+ return is_valid() ? Memory::Write(reinterpret_cast(m_address), data) : false;
+ }
+
+ bool ScanResult::write(const std::vector& data) const
+ {
+ return is_valid() ? Memory::Write(reinterpret_cast(m_address), data) : false;
+ }
+
+ PVOID* ScanResult::hook(PVOID hook_function) const
+ {
+ return (is_valid() && Hooking::HookFunction(&(PVOID&)m_address, hook_function)) ? reinterpret_cast(m_address) : NULL;
+ }
+
+ bool ScanResult::unhook() const
+ {
+ return is_valid() ? Hooking::UnhookFunction(&(PVOID&)m_address) : false;
+ }
+
+ std::vector ScanResult::get_all_references(const std::vector& parsed_pattern, bool calculate_relative_address, uintptr_t base_address, size_t image_size, bool only_first) const
+ {
+ if (base_address == 0) base_address = m_base_address;
+ if (image_size == 0) image_size = m_image_size;
+
+ if (!calculate_relative_address) {
+ std::vector new_parsed_pattern = parsed_pattern;
+ const uint8_t* ptr = reinterpret_cast(&m_address);
+ for (size_t i = 0; i < sizeof(uintptr_t); ++i) {
+ BytePattern bp;
+ uint8_t byte = ptr[i];
+ bp.half_byte[0].data = byte >> 4;
+ bp.half_byte[1].data = byte & 0xF;
+ new_parsed_pattern.push_back(bp);
+ }
+ return ScanAll(base_address, image_size, new_parsed_pattern, only_first);
+ }
+
+#if 0
+ std::vector result;
+ const auto pattern_size = parsed_pattern.size();
+ for (const auto& address : ScanAll(base_address, image_size, parsed_pattern)) {
+ const auto offset_ptr = reinterpret_cast(address.data() + pattern_size);
+ const auto relative_address = address + pattern_size + *offset_ptr + sizeof(int32_t);
+ if (relative_address == m_address) {
+ result.push_back(ScanResult(address, base_address, image_size));
+ if (only_first) break;
+ }
+ }
+ return result;
+#else
+ std::vector result;
+ const uint8_t* start = reinterpret_cast(base_address);
+ const uint8_t* end = start + image_size;
+ const size_t pattern_size = parsed_pattern.size();
+ const BytePattern& first_pattern = parsed_pattern[0];
+
+ for (const uint8_t* it = start; it + pattern_size <= end; ++it) {
+ if ((first_pattern.half_byte[0].wildcard || ((it[0] & 0xF0) == (first_pattern.half_byte[0].data << 4))) &&
+ (first_pattern.half_byte[1].wildcard || ((it[0] & 0x0F) == first_pattern.half_byte[1].data))) {
+
+ bool found = true;
+ for (size_t i = 1; i < pattern_size; ++i) {
+ const BytePattern& pattern = parsed_pattern[i];
+ uint8_t byte = it[i];
+
+ if (!(pattern.half_byte[0].wildcard || ((byte & 0xF0) == (pattern.half_byte[0].data << 4))) ||
+ !(pattern.half_byte[1].wildcard || ((byte & 0x0F) == pattern.half_byte[1].data))) {
+ found = false;
+ break;
+ }
+ }
+
+ if (found) {
+ const auto offset_ptr = reinterpret_cast(const_cast(it + pattern_size));
+ const auto relative_address = reinterpret_cast(it) + pattern_size + *offset_ptr + sizeof(int32_t);
+ if (relative_address == m_address) {
+ result.push_back(ScanResult(reinterpret_cast(it), base_address, image_size));
+ if (only_first) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return result;
+#endif
+ }
+
+ std::vector ScanResult::get_all_references(std::wstring_view pattern, bool calculate_relative_address, uintptr_t base_address, size_t image_size, bool only_first) const
+ {
+ return get_all_references(ParseBytePattern(pattern), calculate_relative_address, base_address, image_size, only_first);
+ }
+
+ ScanResult ScanResult::get_first_reference(const std::vector& parsed_pattern, bool calculate_relative_address, uintptr_t base_address, size_t image_size) const
+ {
+ const auto references = get_all_references(parsed_pattern, calculate_relative_address, base_address, image_size, true);
+ return references.empty() ? ScanResult(0, 0, 0) : references.front();
+ }
+
+ ScanResult ScanResult::get_first_reference(std::wstring_view pattern, bool calculate_relative_address, uintptr_t base_address, size_t image_size) const
+ {
+ return get_first_reference(ParseBytePattern(pattern), calculate_relative_address, base_address, image_size);
+ }
+
+ uintptr_t ScanResult::get_base_address() const
+ {
+ return m_base_address;
+ }
+
+ size_t ScanResult::get_image_size() const
+ {
+ return m_image_size;
+ }
+
+ void ScanResult::print_address() const
+ {
+ Print(L"{:x}", m_address);
+ }
}
\ No newline at end of file
diff --git a/src/BasicUtils/MemoryScanner.h b/src/BasicUtils/MemoryScanner.h
index 3a2f58e..83458cb 100644
--- a/src/BasicUtils/MemoryScanner.h
+++ b/src/BasicUtils/MemoryScanner.h
@@ -1,61 +1,75 @@
-#ifndef MEMORY_SCANNER_H
-#define MEMORY_SCANNER_H
-
-#include
-#include
-#include
-
-namespace MemoryScanner
-{
- struct ModuleInfo {
- std::wstring_view module_name;
- uintptr_t base_address;
- size_t module_size;
- };
-
- class ScanResult {
- public:
- ScanResult() : m_address(0), m_base_address(0), m_image_size(0) {}
- ScanResult(uintptr_t address, uintptr_t base, size_t size);
- operator uintptr_t() const;
-
- bool is_valid(const std::vector& value = {}) const;
- uint8_t* data() const;
- ScanResult rva() const;
- ScanResult offset(std::ptrdiff_t offset_value) const;
- ScanResult scan_first(std::wstring_view value) const;
-
- bool write(const void* data, size_t size) const;
- bool write(std::string_view data) const;
- bool write(std::initializer_list data) const;
-
- PVOID* hook(PVOID hook_function) const;
- bool unhook() const;
-
- std::vector get_all_matching_codes(const std::vector& pattern_byte, bool calculate_relative_offset = true, uintptr_t base_address = 0, size_t image_size = 0, bool only_first = false) const;
- ScanResult get_first_matching_code(const std::vector& pattern_byte, bool calculate_relative_offset = true, uintptr_t base_address = 0, size_t image_size = 0) const;
-
- uintptr_t get_base_address() const;
- size_t get_image_size() const;
-
- void print_address() const;
-
- private:
- uintptr_t m_address;
- uintptr_t m_base_address;
- size_t m_image_size;
- };
-
- ModuleInfo GetModuleInfo(std::wstring_view module_name = {});
- ScanResult GetFunctionAddress(std::string_view module_name, std::string_view function_name);
-
- std::vector ScanAll(uintptr_t base_address, size_t image_size, const std::vector& pattern_byte, bool only_first = false);
- std::vector ScanAll(uintptr_t base_address, size_t image_size, std::wstring_view signature);
- std::vector ScanAll(std::wstring_view signature, std::wstring_view module_name = {});
-
- ScanResult ScanFirst(uintptr_t base_address, size_t image_size, const std::vector& pattern_byte);
- ScanResult ScanFirst(uintptr_t base_address, size_t image_size, std::wstring_view signature);
- ScanResult ScanFirst(std::wstring_view signature, std::wstring_view module_name = {});
-}
-
-#endif // MEMORY_SCANNER_H
+#ifndef MEMORY_SCANNER_H
+#define MEMORY_SCANNER_H
+
+#include
+#include
+#include
+
+namespace MemoryScanner
+{
+ struct ModuleInfo {
+ std::wstring_view module_name;
+ uintptr_t base_address;
+ size_t module_size;
+ };
+
+ struct BytePattern {
+ struct HalfByte {
+ uint8_t data{};
+ bool wildcard{};
+ } half_byte[2]{};
+ };
+
+ class ScanResult {
+ public:
+ ScanResult() : m_address(0), m_base_address(0), m_image_size(0) {}
+ ScanResult(uintptr_t address, uintptr_t base, size_t size, bool is_rva = false);
+ ScanResult(uintptr_t address, std::wstring_view module_name = {}, bool is_rva = false);
+ operator uintptr_t() const;
+
+ bool is_valid(const std::vector& parsed_pattern = {}) const;
+ bool is_valid(std::wstring_view pattern) const;
+ uint8_t* data() const;
+ ScanResult rva() const;
+ ScanResult offset(std::ptrdiff_t offset_value) const;
+ ScanResult scan_first(std::wstring_view value) const;
+
+ bool write(const std::string_view& data) const;
+ bool write(const std::wstring_view& data) const;
+ bool write(const std::initializer_list& data) const;
+ bool write(const std::vector& data) const;
+
+ PVOID* hook(PVOID hook_function) const;
+ bool unhook() const;
+
+ std::vector get_all_references(const std::vector& parsed_pattern, bool calculate_relative_address = true, uintptr_t base_address = 0, size_t image_size = 0, bool only_first = false) const;
+ std::vector get_all_references(std::wstring_view pattern, bool calculate_relative_address = true, uintptr_t base_address = 0, size_t image_size = 0, bool only_first = false) const;
+
+ ScanResult get_first_reference(const std::vector& parsed_pattern, bool calculate_relative_address = true, uintptr_t base_address = 0, size_t image_size = 0) const;
+ ScanResult get_first_reference(std::wstring_view pattern, bool calculate_relative_address = true, uintptr_t base_address = 0, size_t image_size = 0) const;
+
+ uintptr_t get_base_address() const;
+ size_t get_image_size() const;
+
+ void print_address() const;
+
+ private:
+ uintptr_t m_address;
+ uintptr_t m_base_address;
+ size_t m_image_size;
+ };
+
+ ModuleInfo GetModuleInfo(std::wstring_view module_name = {});
+ ScanResult GetFunctionAddress(std::string_view module_name, std::string_view function_name);
+ std::vector ParseBytePattern(std::wstring_view pattern);
+
+ std::vector ScanAll(uintptr_t base_address, size_t image_size, const std::vector& parsed_pattern, bool only_first = false);
+ std::vector ScanAll(uintptr_t base_address, size_t image_size, std::wstring_view pattern);
+ std::vector ScanAll(std::wstring_view pattern, std::wstring_view module_name = {});
+
+ ScanResult ScanFirst(uintptr_t base_address, size_t image_size, const std::vector& parsed_pattern);
+ ScanResult ScanFirst(uintptr_t base_address, size_t image_size, std::wstring_view pattern);
+ ScanResult ScanFirst(std::wstring_view pattern, std::wstring_view module_name = {});
+}
+
+#endif // MEMORY_SCANNER_H
diff --git a/src/BasicUtils/Utils.cpp b/src/BasicUtils/Utils.cpp
index 2cf60ca..0892403 100644
--- a/src/BasicUtils/Utils.cpp
+++ b/src/BasicUtils/Utils.cpp
@@ -1,194 +1,327 @@
-#include "Utils.h"
-
-#include
-#include
-
-namespace Utils
-{
- std::string ToHexString(const std::vector& byte_array, const bool insert_spaces)
- {
- std::ostringstream oss;
- oss << std::hex << std::setfill('0');
-
- for (size_t i = 0; i < byte_array.size(); ++i) {
- if (i > 0 && insert_spaces) {
- oss << ' ';
- }
- oss << std::setw(2) << static_cast(byte_array[i]);
- }
-
- std::string hex_string = oss.str();
- std::transform(hex_string.begin(), hex_string.end(), hex_string.begin(), ::toupper);
-
- return hex_string;
- }
-
- std::string ToHexString(const uint8_t* data, size_t size, const bool insert_spaces)
- {
- if (data == nullptr) {
- PrintError(L"ToHexString: The data pointer is null.");
- return std::string();
- }
-
- if (size == 0)
- size = std::strlen(reinterpret_cast(data));
-
- return ToHexString(std::vector(data, data + size), insert_spaces);
- }
-
- std::wstring ToHexWideString(const std::vector& byte_array, const bool insert_spaces)
- {
- std::wostringstream oss;
- oss << std::hex << std::setfill(L'0');
-
- for (size_t i = 0; i < byte_array.size(); ++i) {
- if (i > 0 && insert_spaces) {
- oss << L' ';
- }
- oss << std::setw(2) << static_cast(byte_array[i]);
- }
-
- std::wstring hex_string = oss.str();
- std::transform(hex_string.begin(), hex_string.end(), hex_string.begin(), ::towupper);
-
- return hex_string;
- }
-
- std::wstring ToHexWideString(const uint8_t* data, size_t size, const bool insert_spaces)
- {
- if (data == nullptr) {
- PrintError(L"ToHexWideString: The data pointer is null.");
- return std::wstring();
- }
-
- if (size == 0)
- size = std::wcslen(reinterpret_cast(data));
-
- return ToHexWideString(std::vector(data, data + size), insert_spaces);
- }
-
- std::string ConvertUInt8ArrayToString(uint8_t* data)
- {
- return std::string(reinterpret_cast(data), reinterpret_cast(data + std::strlen(reinterpret_cast(data))));
- }
-
- std::wstring ConvertUInt8ArrayToWideString(uint8_t* data)
- {
- return std::wstring(reinterpret_cast(data), reinterpret_cast(data + std::wcslen(reinterpret_cast(data))));
- }
-
- std::string IntegerToHexString(uintptr_t integer_value)
- {
- return std::format("{:x}", integer_value);
- }
-
- std::wstring IntegerToHexWideString(uintptr_t integer_value)
- {
- return std::format(L"{:x}", integer_value);
- }
-
- std::string ToString(std::wstring_view wide_string)
- {
- int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wide_string[0], (int)wide_string.size(), NULL, 0, NULL, NULL);
- std::string str_to(size_needed, 0);
- WideCharToMultiByte(CP_UTF8, 0, &wide_string[0], (int)wide_string.size(), &str_to[0], size_needed, NULL, NULL);
- return str_to;
- }
-
- std::wstring ToString(std::string_view narrow_string)
- {
- int size_needed = MultiByteToWideChar(CP_UTF8, 0, &narrow_string[0], (int)narrow_string.size(), NULL, 0);
- std::wstring wstr_to(size_needed, 0);
- MultiByteToWideChar(CP_UTF8, 0, &narrow_string[0], (int)narrow_string.size(), &wstr_to[0], size_needed);
- return wstr_to;
- }
-
- std::wstring ToString(std::u16string_view utf16_string)
- {
- return std::wstring(utf16_string.begin(), utf16_string.end());
- }
-
- bool Contains(std::string_view str1, std::string_view str2, bool case_sensitive)
- {
- auto it = std::search(
- str1.begin(), str1.end(),
- str2.begin(), str2.end(),
- [case_sensitive](char ch1, char ch2) {
- return case_sensitive ? ch1 == ch2 : std::toupper(ch1) == std::toupper(ch2);
- }
- );
- return (it != str1.end());
- }
-
- bool Contains(std::wstring_view str1, std::wstring_view str2, bool case_sensitive)
- {
- auto it = std::search(
- str1.begin(), str1.end(),
- str2.begin(), str2.end(),
- [case_sensitive](wchar_t ch1, wchar_t ch2) {
- return case_sensitive ? ch1 == ch2 : std::toupper(ch1) == std::toupper(ch2);
- }
- );
- return (it != str1.end());
- }
-
- bool Equals(std::string_view str1, std::string_view str2, bool case_sensitive)
- {
- return std::equal(str1.begin(), str1.end(), str2.begin(), str2.end(),
- [case_sensitive](char ch1, char ch2) {
- return case_sensitive ? ch1 == ch2 : std::toupper(ch1) == std::toupper(ch2);
- });
- }
-
- bool Equals(std::wstring_view str1, std::wstring_view str2, bool case_sensitive)
- {
- return std::equal(str1.begin(), str1.end(), str2.begin(), str2.end(),
- [case_sensitive](wchar_t ch1, wchar_t ch2) {
- return case_sensitive ? ch1 == ch2 : std::toupper(ch1) == std::toupper(ch2);
- });
- }
-
- void WriteIniFile(std::wstring_view ini_path, std::wstring_view section, std::wstring_view key, std::wstring_view value)
- {
- WritePrivateProfileStringW(section.data(), key.data(), value.data(), ini_path.data());
- }
-
- std::wstring ReadIniFile(std::wstring_view ini_path, std::wstring_view section, std::wstring_view key)
- {
- wchar_t value[255];
- GetPrivateProfileStringW(section.data(), key.data(), L"", value, 255, ini_path.data());
- return std::wstring(value);
- }
-
-#ifndef NDEBUG
- void MeasureExecutionTime(std::function func)
- {
- const auto start_time = std::chrono::high_resolution_clock::now();
- func();
- const auto end_time = std::chrono::high_resolution_clock::now();
- const auto duration = std::chrono::duration_cast(end_time - start_time).count();
-
- SetConsoleTitleW(FormatString(L"Execution time: {:d}ms", duration).c_str());
- }
-
- void PrintSymbols(std::wstring_view module_name)
- {
- HMODULE hModule = GetModuleHandleW(module_name.data());
- if (!hModule && !(hModule = LoadLibraryW(module_name.data()))) {
- PrintError(L"PrintSymbols: Failed to load module.");
- return;
- }
-
- PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hModule;
- PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)dosHeader + dosHeader->e_lfanew);
- PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)dosHeader + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
- PDWORD functions = (PDWORD)((BYTE*)dosHeader + exportDirectory->AddressOfFunctions);
- PDWORD names = (PDWORD)((BYTE*)dosHeader + exportDirectory->AddressOfNames);
- PWORD ordinals = (PWORD)((BYTE*)dosHeader + exportDirectory->AddressOfNameOrdinals);
-
- for (DWORD i = 0; i < exportDirectory->NumberOfNames; i++) {
- Print(L"{}", reinterpret_cast((BYTE*)dosHeader + names[i]));
- }
- }
-#endif // NDEBUG
+#include "Utils.h"
+
+#include
+#include
+#include
+#include