Merge pull request #5 from josh33901/core-add

Add math, vfunc and hooks and interface grabbing
This commit is contained in:
Marc 2018-03-13 17:05:19 +01:00 committed by GitHub
commit 340a2bf364
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 708 additions and 42 deletions

164
src/hooks.hh Normal file
View File

@ -0,0 +1,164 @@
#pragma once
#include "types.hh"
#include "vfunc.hh"
#include <algorithm>
#include <utility>
#include <vector>
namespace Hooks {
template <typename T, u32 offset>
class HookInstance {
T * instance;
void **original_table;
void **new_table;
// pair from index to old function pointer
std::vector<std::pair<u32, void *>> hooked_functions;
auto count_funcs() {
u32 count = -1;
do
++count;
while (original_table[count] != nullptr);
return count;
}
auto get_table() {
return *(reinterpret_cast<void ***>(instance) + offset);
}
static auto create_table(u32 count) {
return new void *[count];
}
static auto replace_table_pointer(T *instance, void **new_pointer) {
auto table = reinterpret_cast<void ***>(instance) + offset;
*table = new_pointer;
}
public:
HookInstance(T *instance) : instance(instance) {
assert(instance);
original_table = get_table();
assert(original_table);
auto num_funcs = count_funcs();
assert(num_funcs != -1);
new_table = create_table(num_funcs);
// copy the original function pointers over
memcpy(new_table, original_table, num_funcs * 4);
// replace the original with our own
replace_table_pointer(instance, new_table);
}
~HookInstance() {
replace_table_pointer(instance, original_table);
assert(new_table);
delete[] new_table;
}
auto hook_function(u32 index, void *f) {
assert(index < count_funcs());
hooked_functions.push_back(std::make_pair(index, f));
new_table[index] = f;
}
auto unhook_function(u32 index) -> void {
assert(index < count_funcs());
auto found = false;
for (auto &f : hooked_functions) {
if (f.first == index) {
found = true;
// remove from the hooked functions list
std::swap(f, hooked_functions.back());
hooked_functions.pop_back();
// swap the pointers
new_table[index] = original_table[index];
// at this point there is no reason to swap the tables over
// either the tables are already swapped in which case this
// has an immediate effect, or they are not in which case its
// not a problem anyway.
return;
}
}
assert(found);
}
template <typename F>
auto get_original_function(u32 index) {
assert(index < count_funcs());
return reinterpret_cast<F>(original_table[index]);
}
auto get_instance() {
return instance;
}
};
template <typename T, u32 offset>
class HookFunction {
static std::vector<HookInstance<T, offset> *> hooks;
auto create_hook_instance(T *instance) {
auto h = new HookInstance<T, offset>(instance);
hooks.push_back(h);
return h;
}
T * instance;
u32 index;
public:
HookFunction(T *instance, u32 index, void *f) {
assert(instance);
HookInstance<T, offset> *val = nullptr;
for (auto &v : hooks) {
if (v->get_instance() == instance) {
val = v;
break;
}
}
if (val == nullptr) {
// we havent hooked this instance yet, so do that now
create_hook_instance(instance)->hook_function(index, f);
return;
}
this->instance = instance;
this->index = index;
val->hook_function(index, f);
}
~HookFunction() {
for (auto &v : hooks) {
if (v->get_instance() == instance) {
v->unhook_function(index);
return;
}
}
}
};
template <typename T, u32 offset>
std::vector<HookInstance<T, offset> *> HookFunction<T, offset>::hooks;
} // namespace Hooks

44
src/interface.cc Normal file
View File

@ -0,0 +1,44 @@
#include "stdafx.hh"
#include "interface.hh"
#include "signature.hh"
using InstantiateInterfaceFn = void *(*)();
class InterfaceReg {
public:
InterfaceReg() = delete;
public:
InstantiateInterfaceFn create_fn;
const char * name;
InterfaceReg *next;
};
void *InterfaceHelpers::find_interface(const char *module_name, const char *interface_name) {
InterfaceReg *interface_reg_head;
if constexpr (DoghookPlatform::windows()) {
interface_reg_head = **Signature::find_pattern<InterfaceReg ***>(module_name, "8B 35 ? ? ? ? 57 85 F6 74 38", 2);
} else if constexpr (DoghookPlatform::linux()) {
} else if constexpr (DoghookPlatform::osx()) {
}
assert(interface_reg_head);
for (auto r = interface_reg_head; r != nullptr; r = r->next) {
auto match = strstr(r->name, interface_name);
// Only match if the next character after the name is the number
if (match != nullptr && !isalpha(*(match + strlen(interface_name)))) return r->create_fn();
}
// if you get here either
// 1: your interface name is wrong
// 2: your dll name is wrong
assert(0);
return nullptr;
}

45
src/interface.hh Normal file
View File

@ -0,0 +1,45 @@
#pragma once
namespace InterfaceHelpers {
// this auto-resolves the library using Signature::
void *find_interface(const char *module_name, const char *interface_name);
} // namespace InterfaceHelpers
template <typename T>
class Interface {
static T *value;
public:
// interface name should be the name of the interface
// without the version number
// e.g. "VClient017" -> "VClient"
static auto set_from_interface(const char *module_name, const char *interface_name) {
value = static_cast<T *>(
InterfaceHelpers::find_interface(module_name, interface_name));
}
// set from a pointer (you should only do this for non-exported
// interfaces e.g. IInput or CClientModeShared)
static auto set_from_pointer(T *new_value) {
value = new_value;
}
auto operator-> () -> T *& {
return value;
}
auto get() -> T *& {
return value;
}
operator bool() {
return value != nullptr;
}
};
template <typename T>
T *Interface<T>::value;
template <typename T>
using IFace = Interface<T>;

353
src/math.hh Normal file
View File

@ -0,0 +1,353 @@
#pragma once
#include "platform.hh"
#include <cassert>
#include <cmath>
#include <float.h>
#undef max
#undef min
namespace Math {
constexpr auto PI = 3.14159265358979323846f;
inline auto to_radians(float x) {
return x * (PI / 180.0f);
}
inline auto to_degrees(float x) {
return x * (180.0f / PI);
}
template <typename T>
auto lerp(float percent, T min, T max) {
return min + ((max - min) * percent);
}
class Vector {
public:
float x, y, z;
// sentinal values
inline static auto origin() { return Vector(0); }
inline static auto zero() { return Vector(0); }
inline static auto invalid() { return Vector(FLT_MAX); }
inline Vector() {}
inline Vector(float x, float y, float z) : x(x), y(y), z(z) {}
inline Vector(float v) : x(v), y(v), z(v) {}
inline Vector(const Vector &v) : x(v.x), y(v.y), z(v.z) {}
// equality
inline auto operator==(const Vector &v) const {
return (x == v.x) && (y == v.y) && (z == v.z);
}
inline auto operator!=(const Vector &v) const {
return (x != v.x) || (y != v.y) || (z != v.z);
}
// arithmetic operations
inline auto operator+=(const Vector &v) {
x += v.x;
y += v.y;
z += v.z;
return *this;
}
inline auto operator-=(const Vector &v) {
x -= v.x;
y -= v.y;
z -= v.z;
return *this;
}
inline auto operator*=(const Vector &v) {
x *= v.x;
y *= v.y;
z *= v.z;
return *this;
}
inline auto operator*=(float s) {
x *= s;
y *= s;
z *= s;
return *this;
}
inline auto operator/=(const Vector &v) {
assert(v.x != 0.0f && v.y != 0.0f && v.z != 0.0f);
x /= v.x;
y /= v.y;
z /= v.z;
return *this;
}
inline auto operator/=(float s) {
assert(s != 0.0f);
float oofl = 1.0f / s;
x *= oofl;
y *= oofl;
z *= oofl;
return *this;
}
inline auto operator-(void) const {
return Vector(-x, -y, -z);
}
inline auto operator+(const Vector &v) const {
return Vector(*this) += v;
}
inline auto operator-(const Vector &v) const {
return Vector(*this) -= v;
}
auto operator*(const Vector &v) const {
return Vector(*this) *= v;
}
inline auto operator/(const Vector &v) const {
return Vector(*this) /= v;
}
inline auto operator*(float s) const {
return Vector(*this) *= s;
}
inline auto operator/(float s) const {
return Vector(*this) /= s;
}
inline auto length_sqr() const {
return (x * x) + (y * y) + (z * z);
}
inline auto length() const {
return std::sqrt(length_sqr());
}
inline auto distance(const Vector &b) const {
auto d = *this - b;
return d.length();
}
inline auto dot(Vector &b) const {
return (x * b.x) + (y * b.y) + (z * b.z);
}
inline auto cross(Vector &v) const {
auto res = Vector::invalid();
res.x = y * v.z - z * v.y;
res.y = z * v.x - x * v.z;
res.z = x * v.y - y * v.x;
return res;
}
inline auto to_angle() const {
float tmp, yaw, pitch;
if (y == 0 && x == 0) {
yaw = 0;
if (z > 0)
pitch = 270;
else
pitch = 90;
} else {
yaw = (atan2(y, x) * 180 / PI);
if (yaw < 0)
yaw += 360;
tmp = sqrt(x * x + y * y);
pitch = (atan2(-z, tmp) * 180 / PI);
if (pitch < 0)
pitch += 360;
}
return Vector(pitch, yaw, 0);
}
inline auto to_vector() const {
float sp, sy, cp, cy;
sy = sinf(to_radians(y));
cy = cosf(to_radians(y));
sp = sinf(to_radians(x));
cp = cosf(to_radians(x));
return Vector(cp * cy, cp * sy, -sp);
}
inline auto lerp(const Vector &other, float percent) const {
Vector ret;
ret.x = x + (other.x - x) * percent;
ret.y = y + (other.y - y) * percent;
ret.z = z + (other.z - z) * percent;
return ret;
}
};
class Matrix3x4 {
public:
Matrix3x4() {}
Matrix3x4(
float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23) {
data[0][0] = m00;
data[0][1] = m01;
data[0][2] = m02;
data[0][3] = m03;
data[1][0] = m10;
data[1][1] = m11;
data[1][2] = m12;
data[1][3] = m13;
data[2][0] = m20;
data[2][1] = m21;
data[2][2] = m22;
data[2][3] = m23;
}
// Creates a matrix where the X axis = forward
// the Y axis = left, and the Z axis = up
void init(const Vector &x_axis, const Vector &y_axis, const Vector &z_axis, const Vector &origin) {
data[0][0] = x_axis.x;
data[0][1] = y_axis.x;
data[0][2] = z_axis.x;
data[0][3] = origin.x;
data[1][0] = x_axis.y;
data[1][1] = y_axis.y;
data[1][2] = z_axis.y;
data[1][3] = origin.y;
data[2][0] = x_axis.z;
data[2][1] = y_axis.z;
data[2][2] = z_axis.z;
data[2][3] = origin.z;
}
// Creates a matrix where the X axis = forward
// the Y axis = left, and the Z axis = up
Matrix3x4(const Vector &x_axis, const Vector &y_axis, const Vector &z_axis, const Vector &origin) {
init(x_axis, y_axis, z_axis, origin);
}
inline void invalidate(void) {
assert(0);
// for (int i = 0; i < 3; i++) {
// for (int j = 0; j < 4; j++) {
// data[i][j] = VEC_T_NAN;
// }
// }
}
float *operator[](int i) {
assert((i >= 0) && (i < 3));
return data[i];
}
const float *operator[](int i) const {
assert((i >= 0) && (i < 3));
return data[i];
}
float * base() { return &data[0][0]; }
const float *base() const { return &data[0][0]; }
const auto get_column(u32 column, Vector &out) const {
out.x = data[0][column];
out.y = data[1][column];
out.z = data[2][column];
}
const auto get_row(u32 row, Vector &out) const {
out.x = data[row][0];
out.y = data[row][1];
out.z = data[row][2];
}
//
auto rotate_vector(const Math::Vector &in) const {
Vector out;
Vector row;
out.x = in.dot((get_row(0, row), row));
out.y = in.dot((get_row(1, row), row));
out.z = in.dot((get_row(2, row), row));
return out;
}
auto from_angle(const Vector &angles) {
auto radian_pitch = to_radians(angles.x);
auto radian_yaw = to_radians(angles.y);
auto radian_roll = to_radians(angles.z);
auto sp = sin(radian_pitch);
auto cp = cos(radian_pitch);
auto sy = sin(radian_yaw);
auto cy = cos(radian_yaw);
auto sr = sin(radian_roll);
auto cr = cos(radian_roll);
// matrix = (YAW * PITCH) * ROLL
data[0][0] = cp * cy;
data[1][0] = cp * sy;
data[2][0] = -sp;
float crcy = cr * cy;
float crsy = cr * sy;
float srcy = sr * cy;
float srsy = sr * sy;
data[0][1] = sp * srcy - crsy;
data[1][1] = sp * srsy + crcy;
data[2][1] = sr * cp;
data[0][2] = (sp * crcy + srsy);
data[1][2] = (sp * crsy - srcy);
data[2][2] = cr * cp;
data[0][3] = 0.0f;
data[1][3] = 0.0f;
data[2][3] = 0.0f;
}
float data[3][4];
};
// TODO: make these member functions
inline void matrix_angles(const Matrix3x4 &matrix, float *angles) {
float forward[3];
float left[3];
float up[3];
//
// Extract the basis vectors from the matrix. Since we only need the Z
// component of the up vector, we don't get X and Y.
//
forward[0] = matrix[0][0];
forward[1] = matrix[1][0];
forward[2] = matrix[2][0];
left[0] = matrix[0][1];
left[1] = matrix[1][1];
left[2] = matrix[2][1];
up[2] = matrix[2][2];
float xyDist = std::sqrt(forward[0] * forward[0] + forward[1] * forward[1]);
// enough here to get angles?
if (xyDist > 0.001f) {
// (yaw) y = ATAN( forward.y, forward.x ); -- in our space, forward is the X axis
angles[1] = to_degrees(atan2f(forward[1], forward[0]));
// (pitch) x = ATAN( -forward.z, sqrt(forward.x*forward.x+forward.y*forward.y) );
angles[0] = to_degrees(atan2f(-forward[2], xyDist));
// (roll) z = ATAN( left.z, up.z );
angles[2] = to_degrees(atan2f(left[2], up[2]));
} else {
// (yaw) y = ATAN( -left.x, left.y ); -- forward is mostly z, so use right for yaw
angles[1] = to_degrees(atan2f(-left[0], left[1]));
// (pitch) x = ATAN( -forward.z, sqrt(forward.x*forward.x+forward.y*forward.y) );
angles[0] = to_degrees(atan2f(-forward[2], xyDist));
// Assume no roll in this case as one degree of freedom has been lost (i.e. yaw == roll)
angles[2] = 0;
}
}
inline void matrix_angles(const Matrix3x4 &matrix, Vector &angles, Vector &position) {
matrix.get_column(3, position);
matrix_angles(matrix, &angles.x);
}
} // namespace Math

View File

@ -40,15 +40,15 @@
#endif #endif
// clang-format on // clang-format on
namespace DogHookPlatform { namespace DoghookPlatform {
constexpr bool windows() { return doghook_platform_windows(); } inline constexpr bool windows() { return doghook_platform_windows(); }
constexpr bool linux() { return doghook_platform_linux(); } inline constexpr bool linux() { return doghook_platform_linux(); }
constexpr bool osx() { return doghook_platform_osx(); } inline constexpr bool osx() { return doghook_platform_osx(); }
constexpr bool msvc() { return doghook_platform_msvc(); } inline constexpr bool msvc() { return doghook_platform_msvc(); }
constexpr bool clang() { return doghook_platform_clang(); } inline constexpr bool clang() { return doghook_platform_clang(); }
constexpr bool gcc() { return doghook_platform_gcc(); } inline constexpr bool gcc() { return doghook_platform_gcc(); }
} // namespace DogHookPlatform } // namespace DoghookPlatform
#include "types.hh" #include "types.hh"

View File

@ -135,7 +135,7 @@ auto Signature::resolve_library(const char *name) -> void * {
auto dir = readdir(d); auto dir = readdir(d);
while (dir != nullptr) { while (dir != nullptr) {
if (dir->d_type == DT_REG && strstr(dir->d_name, to_find) != nullptr && strstr(dir->d_name, ".so") != nullptr) { if (dir->d_type == DT_REG && strstr(dir->d_name, to_find) && strstr(dir->d_name, ".so")) {
sprintf(out, "%s/%s", dirname, dir->d_name); sprintf(out, "%s/%s", dirname, dir->d_name);
return true; return true;
} }

View File

@ -16,37 +16,4 @@ auto find_pattern(const char *module, const char *pattern, u32 offset) {
return reinterpret_cast<T>(find_pattern(module, pattern) + offset); return reinterpret_cast<T>(find_pattern(module, pattern) + offset);
} }
void *resolve_callgate(void *address); void *resolve_callgate(void *address);
// TODO: there must be some way we can do this at init time instead of using magic statics.
// make a static chain and find them at level_init or at init_all
class Pattern {
const char *module;
const char *signature;
public:
Pattern(const char * module,
[[maybe_unused]] const char *signature_windows,
[[maybe_unused]] const char *signature_linux,
[[maybe_unused]] const char *signature_osx)
: module(module),
#if doghook_platform_windows()
signature(signature_windows)
#elif doghook_platform_linux()
signature(signature_linux)
#elif doghook_platform_osx()
signature(signature_osx)
#endif
{
assert(signature[0] != '\0');
}
auto find() -> u8 * {
return ::Signature::find_pattern(module, signature);
}
template <typename T>
auto find(u32 offset) -> T {
return ::Signature::find_pattern<T>(module, signature, offset);
}
};
} // namespace Signature } // namespace Signature

View File

@ -13,3 +13,5 @@ using i16 = int16_t;
using i32 = int32_t; using i32 = int32_t;
using i64 = int64_t; using i64 = int64_t;
using iptr = intptr_t; using iptr = intptr_t;
#include "math.hh"

91
src/vfunc.hh Normal file
View File

@ -0,0 +1,91 @@
#pragma once
#include <functional>
#include <type_traits>
#include "platform.hh"
#include "types.hh"
// helpers for calling virtual functions
namespace VFunc {
inline auto get_table(void *inst, u32 offset) -> void ** {
return *reinterpret_cast<void ***>(reinterpret_cast<u8 *>(inst) + offset);
}
inline auto get_table(const void *inst, u32 offset) -> const void ** {
return *reinterpret_cast<const void ***>(
reinterpret_cast<u8 *>(
const_cast<void *>(inst)) +
offset);
}
template <typename F>
inline auto get_func(const void *inst, u32 index, u32 offset) {
return reinterpret_cast<F>(get_table(inst, offset)[index]);
}
template <typename F>
inline auto get_func(void *inst, u32 index, u32 offset) {
return reinterpret_cast<F>(get_table(inst, offset)[index]);
}
template <typename>
class Func;
template <typename ObjectType, typename Ret, typename... Args>
class Func<Ret (ObjectType::*)(Args...)> {
using function_type = Ret(__thiscall *)(ObjectType *, Args...);
function_type f;
// On windows this will take into account the offset
// So we do not need to store that seperately
ObjectType *instance;
public:
// TODO: do any other platforms have this offset and is it useful?
Func(ObjectType *instance,
u32 index_windows,
u32 index_linux,
u32 index_osx,
u32 offset_windows) {
// NOTE: this assert is disabled as it is in some really tight loops!
// This should never go off and if it does you will get a nice and obvious fatal crash...
//assert(instance != nullptr);
auto index = 0u;
if constexpr (BluePlatform::windows())
index = index_windows;
else if constexpr (BluePlatform::linux())
index = index_linux;
else if constexpr (BluePlatform::osx())
index = index_osx;
auto offset = 0u;
if constexpr (BluePlatform::windows()) {
offset = offset_windows;
this->instance = reinterpret_cast<ObjectType *>(
reinterpret_cast<u8 *>(instance) + offset);
} else {
this->instance = instance;
}
f = get_func<function_type>(instance, index, offset);
}
auto invoke(Args... args) -> Ret {
return f(instance, args...);
}
};
} // namespace VFunc
// macro for easier definitions of wrapper calls
// name is the name of the function
// off is the windows offset
// varags are the arguments of the parent function
#define return_virtual_func(name, windows, linux, osx, off, ...) \
using c = std::remove_reference<decltype(*this)>::type; \
using t = decltype(&c::name); \
return VFunc::Func<t>(this, windows, linux, osx, off).invoke(__VA_ARGS__)