Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ZeroMemes committed Dec 6, 2022
0 parents commit 18d6be0
Show file tree
Hide file tree
Showing 22 changed files with 894 additions and 0 deletions.
34 changes: 34 additions & 0 deletions CMakeLists.txt
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>)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# libhat
8 changes: 8 additions & 0 deletions include/libhat.h
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>
46 changes: 46 additions & 0 deletions include/libhat/CompileTime.hpp
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;
}
}
32 changes: 32 additions & 0 deletions include/libhat/Defines.hpp
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
34 changes: 34 additions & 0 deletions include/libhat/MemoryProtector.hpp
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
};
}
28 changes: 28 additions & 0 deletions include/libhat/Process.hpp
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>;
}
15 changes: 15 additions & 0 deletions include/libhat/ScanMode.hpp
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);
}
81 changes: 81 additions & 0 deletions include/libhat/Scanner.hpp
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
);
}
64 changes: 64 additions & 0 deletions include/libhat/Signature.hpp
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;
}
}
14 changes: 14 additions & 0 deletions include/libhat/System.hpp
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();
}
12 changes: 12 additions & 0 deletions src/Process.cpp
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));
}
}
Loading

0 comments on commit 18d6be0

Please sign in to comment.