-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 18d6be0
Showing
22 changed files
with
894 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
cmake_minimum_required(VERSION 3.0) | ||
|
||
set(CMAKE_CXX_STANDARD 20) | ||
|
||
project(libhat) | ||
|
||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") | ||
set_source_files_properties(src/arch/x86/AVX2.cpp PROPERTIES COMPILE_FLAGS "/arch:AVX2") | ||
set_source_files_properties(src/arch/x86/AVX512.cpp PROPERTIES COMPILE_FLAGS "/arch:AVX512") | ||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") | ||
set_source_files_properties(src/arch/x86/AVX2.cpp PROPERTIES COMPILE_FLAGS "-mavx -mavx2 -mbmi") | ||
set_source_files_properties(src/arch/x86/AVX512.cpp PROPERTIES COMPILE_FLAGS "-mavx512f -mavx512bw -mbmi") | ||
set_source_files_properties(src/arch/x86/System.cpp PROPERTIES COMPILE_FLAGS "-mxsave") | ||
endif () | ||
|
||
set(LIBHAT_SRC | ||
src/Process.cpp | ||
src/Scanner.cpp | ||
src/System.cpp | ||
|
||
src/os/Win32.cpp | ||
|
||
src/arch/x86/AVX2.cpp | ||
src/arch/x86/AVX512.cpp | ||
src/arch/x86/System.cpp | ||
|
||
src/arch/arm/Neon.cpp | ||
src/arch/arm/System.cpp) | ||
|
||
add_library(libhat ${LIBHAT_SRC}) | ||
|
||
target_include_directories(libhat PUBLIC | ||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/> | ||
$<INSTALL_INTERFACE:include>) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# libhat |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#pragma once | ||
|
||
#include <libhat/CompileTime.hpp> | ||
#include <libhat/MemoryProtector.hpp> | ||
#include <libhat/Process.hpp> | ||
#include <libhat/Signature.hpp> | ||
#include <libhat/Scanner.hpp> | ||
#include <libhat/System.hpp> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#pragma once | ||
|
||
#include <algorithm> | ||
#include <string> | ||
#include <stdexcept> | ||
|
||
namespace hat { | ||
|
||
template<size_t N> | ||
struct string_literal { | ||
constexpr string_literal(const char (&str)[N]) { | ||
std::copy_n(str, N, value); | ||
} | ||
|
||
[[nodiscard]] constexpr const char* c_str() const { | ||
return (const char*) &this->value[0]; | ||
} | ||
|
||
char value[N]; | ||
}; | ||
|
||
static constexpr int atoi(std::string_view str, int base = 10) { | ||
if (base < 2 || base > 36) { | ||
throw std::invalid_argument("Invalid base specified"); | ||
} | ||
|
||
int value = 0; | ||
auto digits = base < 10 ? base : 10; | ||
auto letters = base > 10 ? base - 10 : 0; | ||
|
||
for (char ch : str) { | ||
value *= base; | ||
if (ch >= '0' && ch < '0' + digits) { | ||
value += (ch - '0'); | ||
} else if (ch >= 'A' && ch < 'A' + letters) { | ||
value += (ch - 'A' + 10); | ||
} else if (ch >= 'a' && ch < 'a' + letters) { | ||
value += (ch - 'a' + 10); | ||
} else { | ||
// Throws an exception at runtime AND prevents constexpr evaluation | ||
throw std::invalid_argument("Unexpected character in integer string"); | ||
} | ||
} | ||
return value; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#pragma once | ||
|
||
// Detect CPU Architecture | ||
#if defined(_M_X64) || defined(__amd64__) || defined(_M_IX86) || defined(__i386__) | ||
#define LIBHAT_X86 | ||
#if defined(_M_X64) || defined(__amd64__) | ||
#define LIBHAT_X86_64 | ||
#endif | ||
#elif defined(_M_ARM64) || defined(__aarch64__) || defined(_M_ARM) || defined(__arm__) | ||
#define LIBHAT_ARM | ||
#else | ||
#error Unsupported Architecture | ||
#endif | ||
|
||
// Detect Operating System | ||
#if defined(_WIN32) | ||
#define LIBHAT_WINDOWS | ||
#else | ||
#error Unsupported Operating System | ||
#endif | ||
|
||
// Macros wrapping intrinsics | ||
#ifdef LIBHAT_X86 | ||
#ifdef LIBHAT_X86_64 | ||
#define LIBHAT_TZCNT64(num) _tzcnt_u64(num) | ||
#define LIBHAT_BLSR64(num) _blsr_u64(num) | ||
#else | ||
#include <bit> | ||
#define LIBHAT_TZCNT64(num) std::countl_zero(num) | ||
#define LIBHAT_BLSR64(num) num & (num - 1) | ||
#endif | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <type_traits> | ||
|
||
namespace hat { | ||
|
||
enum class protection : uint8_t { | ||
Read = 0b001, | ||
Write = 0b010, | ||
Execute = 0b100 | ||
}; | ||
|
||
constexpr protection operator|(protection lhs, protection rhs) { | ||
using U = std::underlying_type_t<protection>; | ||
return static_cast<protection>(static_cast<U>(lhs) | static_cast<U>(rhs)); | ||
} | ||
|
||
constexpr protection operator&(protection lhs, protection rhs) { | ||
using U = std::underlying_type_t<protection>; | ||
return static_cast<protection>(static_cast<U>(lhs) & static_cast<U>(rhs)); | ||
} | ||
|
||
/// RAII wrapper for setting memory protection flags | ||
class memory_protector { | ||
public: | ||
memory_protector(uintptr_t address, size_t size, protection flags); | ||
~memory_protector(); | ||
private: | ||
uintptr_t address; | ||
size_t size; | ||
uint32_t oldProtection; // Memory protection flags native to Operating System | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#pragma once | ||
|
||
#include <ranges> | ||
#include <string_view> | ||
|
||
namespace hat::process { | ||
|
||
// TODO: Consider using a typedef or class instead? idk | ||
enum class module_t : uintptr_t {}; | ||
|
||
/// Returns the module for the curent process's base executable | ||
auto get_process_module() -> module_t; | ||
|
||
/// Returns a module by its given name in the current process | ||
auto get_module(std::string_view name) -> module_t; | ||
|
||
/// Returns the module located at the specified base address | ||
auto module_at(uintptr_t address) -> module_t; | ||
|
||
/// Returns the module located at the specified base address | ||
auto module_at(std::byte* address) -> module_t; | ||
|
||
/// Returns the complete memory region for the given module. This may include portions which are uncommitted. | ||
auto get_module_data(module_t module) -> std::span<std::byte>; | ||
|
||
/// Returns the memory region for a named section in the given module | ||
auto get_section_data(module_t module, std::string_view name) -> std::span<std::byte>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#pragma once | ||
|
||
namespace hat { | ||
|
||
enum class scan_mode { | ||
Search, | ||
FastFirst, | ||
AVX2, | ||
AVX512, | ||
Neon | ||
}; | ||
|
||
template<scan_mode> | ||
scan_result find_pattern(std::byte* begin, std::byte* end, signature_view signature); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#pragma once | ||
|
||
#include <algorithm> | ||
#include <array> | ||
#include <utility> | ||
|
||
#include "Process.hpp" | ||
#include "Signature.hpp" | ||
|
||
namespace hat { | ||
|
||
class scan_result { | ||
using rel_t = int32_t; | ||
public: | ||
constexpr scan_result() : result(nullptr) {} | ||
constexpr scan_result(std::byte* result) : result(result) {} // NOLINT(google-explicit-constructor) | ||
|
||
/// Reads an integer of the specified type located at an offset from the signature result | ||
template<std::integral Int> | ||
[[nodiscard]] constexpr Int read(size_t offset) const { | ||
return *reinterpret_cast<Int*>(this->result + offset); | ||
} | ||
|
||
/// Reads an integer of the specified type which represents an index into an array with the given element type | ||
template<std::integral Int, typename ArrayType> | ||
[[nodiscard]] constexpr size_t index(size_t offset) const { | ||
return static_cast<size_t>(read<Int>(offset)) / sizeof(ArrayType); | ||
} | ||
|
||
/// Resolve the relative address located at an offset from the signature result | ||
[[nodiscard]] constexpr std::byte* rel(size_t offset) const { | ||
return this->has_result() ? this->result + this->read<rel_t>(offset) + offset + sizeof(rel_t) : nullptr; | ||
} | ||
|
||
[[nodiscard]] constexpr bool has_result() const { | ||
return this->result != nullptr; | ||
} | ||
|
||
[[nodiscard]] constexpr std::byte* operator*() const { | ||
return this->result; | ||
} | ||
|
||
[[nodiscard]] constexpr std::byte* get() const { | ||
return this->result; | ||
} | ||
private: | ||
std::byte* result; | ||
}; | ||
|
||
enum class compiler_type { | ||
MSVC, | ||
MinGW | ||
}; | ||
|
||
/// Gets the VTable address for a class by its mangled name | ||
template<compiler_type compiler> | ||
scan_result find_vtable( | ||
const std::string& className, | ||
process::module_t module = process::get_process_module() | ||
); | ||
|
||
/// Perform a signature scan on the entirety of the process module or a specified module | ||
scan_result find_pattern( | ||
signature_view signature, | ||
process::module_t module = process::get_process_module() | ||
); | ||
|
||
/// Perform a signature scan on a specific section of the process module or a specified module | ||
scan_result find_pattern( | ||
signature_view signature, | ||
std::string_view section, | ||
process::module_t module = process::get_process_module() | ||
); | ||
|
||
/// Root implementation of FindPattern | ||
scan_result find_pattern( | ||
std::byte* begin, | ||
std::byte* end, | ||
signature_view signature | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
#pragma once | ||
|
||
#include <optional> | ||
#include <ranges> | ||
#include <string_view> | ||
#include <vector> | ||
|
||
#include "CompileTime.hpp" | ||
|
||
namespace hat { | ||
|
||
using signature_element = std::optional<std::byte>; | ||
using signature = std::vector<signature_element>; | ||
using signature_view = std::span<const signature_element>; | ||
|
||
template<size_t N> | ||
using fixed_signature = std::array<signature_element, N>; | ||
|
||
template<typename T> | ||
inline signature object_to_signature(const T& value) { | ||
auto bytes = reinterpret_cast<const std::byte*>(&value); | ||
return {bytes, bytes + sizeof(T)}; | ||
} | ||
|
||
/// Convert raw byte storage into a signature | ||
constexpr signature bytes_to_signature(std::span<const std::byte> bytes) { | ||
return {bytes.begin(), bytes.end()}; | ||
} | ||
|
||
inline signature string_to_signature(std::string_view str) { | ||
return bytes_to_signature({reinterpret_cast<const std::byte*>(str.data()), str.size()}); | ||
} | ||
|
||
constexpr signature parse_signature(std::string_view str) { | ||
signature sig{}; | ||
for (const auto& word : str | std::views::split(' ')) { | ||
if (word.empty()) { | ||
continue; | ||
} else if (word[0] == '?') { | ||
if (sig.empty()) { | ||
throw std::invalid_argument("First byte cannot be a wildcard"); | ||
} | ||
sig.emplace_back(std::nullopt); | ||
} else { | ||
const auto sv = std::string_view{word.begin(), word.end()}; | ||
sig.emplace_back(static_cast<std::byte>(atoi(sv, 16) & 0xFF)); | ||
} | ||
} | ||
return sig; | ||
} | ||
|
||
/// Parses a signature string at compile time, and provides a signature_view which exists for the program's lifetime | ||
template<string_literal str> | ||
inline signature_view compile_signature() { | ||
static constexpr auto compiled = ([]() consteval -> auto { | ||
const auto sig = parse_signature(str.c_str()); | ||
constexpr auto N = parse_signature(str.c_str()).size(); | ||
fixed_signature<N> arr{}; | ||
std::ranges::move(sig, arr.begin()); | ||
return arr; | ||
})(); | ||
return compiled; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#pragma once | ||
|
||
#include "Defines.hpp" | ||
|
||
#if defined(LIBHAT_X86) | ||
#include "../../src/arch/x86/System.hpp" | ||
#elif defined(LIBHAT_ARM) | ||
#include "../../src/arch/arm/System.hpp" | ||
#endif | ||
|
||
namespace hat { | ||
|
||
const system_info& get_system(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#include <libhat/Process.hpp> | ||
|
||
namespace hat::process { | ||
|
||
module_t module_at(uintptr_t address) { | ||
return module_t{address}; | ||
} | ||
|
||
module_t module_at(std::byte* address) { | ||
return module_at(reinterpret_cast<uintptr_t>(address)); | ||
} | ||
} |
Oops, something went wrong.