From 433b9ca979d61fc3c05d78b01df3489e8d2af5ec Mon Sep 17 00:00:00 2001 From: F1ssi0N Date: Tue, 13 Mar 2018 15:43:18 +0000 Subject: [PATCH 1/2] Add math, vfunc and hooks and interface grabbing --- src/hooks.hh | 164 ++++++++++++++++++++++ src/interface.cc | 46 ++++++ src/interface.hh | 45 ++++++ src/math.hh | 353 +++++++++++++++++++++++++++++++++++++++++++++++ src/platform.hh | 16 +-- src/signature.cc | 2 +- src/signature.hh | 33 ----- src/types.hh | 2 + src/vfunc.hh | 91 ++++++++++++ 9 files changed, 710 insertions(+), 42 deletions(-) create mode 100644 src/hooks.hh create mode 100644 src/interface.cc create mode 100644 src/interface.hh create mode 100644 src/math.hh create mode 100644 src/vfunc.hh diff --git a/src/hooks.hh b/src/hooks.hh new file mode 100644 index 0000000..df9e61d --- /dev/null +++ b/src/hooks.hh @@ -0,0 +1,164 @@ +#pragma once + +#include "types.hh" +#include "vfunc.hh" + +#include +#include +#include + +namespace Hooks { +template +class HookInstance { + + T * instance; + void **original_table; + void **new_table; + + // pair from index to old function pointer + std::vector> hooked_functions; + + auto count_funcs() { + u32 count = -1; + do + ++count; + while (original_table[count] != nullptr); + + return count; + } + + auto get_table() { + return *(reinterpret_cast(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(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 + auto get_original_function(u32 index) { + assert(index < count_funcs()); + return reinterpret_cast(original_table[index]); + } + + auto get_instance() { + return instance; + } +}; + +template +class HookFunction { + static std::vector *> hooks; + + auto create_hook_instance(T *instance) { + auto h = new HookInstance(instance); + hooks.push_back(h); + return h; + } + + T * instance; + u32 index; + +public: + HookFunction(T *instance, u32 index, void *f) { + assert(instance); + HookInstance *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 +std::vector *> HookFunction::hooks; + +} // namespace Hooks diff --git a/src/interface.cc b/src/interface.cc new file mode 100644 index 0000000..ef1fb8f --- /dev/null +++ b/src/interface.cc @@ -0,0 +1,46 @@ +#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(module_name, "8B 35 ? ? ? ? 57 85 F6 74 38", 2); + } else if constexpr (DoghookPlatform::linux()) { + static_assert(!DoghookPlatform::linux()); + } else if constexpr (DoghookPlatform::osx()) { + static_assert(!DoghookPlatform::linux()); + } + + 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; +} diff --git a/src/interface.hh b/src/interface.hh new file mode 100644 index 0000000..d35105d --- /dev/null +++ b/src/interface.hh @@ -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 +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( + 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 +T *Interface::value; + +template +using IFace = Interface; diff --git a/src/math.hh b/src/math.hh new file mode 100644 index 0000000..0ef4e30 --- /dev/null +++ b/src/math.hh @@ -0,0 +1,353 @@ +#pragma once + +#include "platform.hh" + +#include +#include +#include + +#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 +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 diff --git a/src/platform.hh b/src/platform.hh index 49b9bb0..28a3b1b 100644 --- a/src/platform.hh +++ b/src/platform.hh @@ -40,15 +40,15 @@ #endif // clang-format on -namespace DogHookPlatform { -constexpr bool windows() { return doghook_platform_windows(); } -constexpr bool linux() { return doghook_platform_linux(); } -constexpr bool osx() { return doghook_platform_osx(); } +namespace DoghookPlatform { +inline constexpr bool windows() { return doghook_platform_windows(); } +inline constexpr bool linux() { return doghook_platform_linux(); } +inline constexpr bool osx() { return doghook_platform_osx(); } -constexpr bool msvc() { return doghook_platform_msvc(); } -constexpr bool clang() { return doghook_platform_clang(); } -constexpr bool gcc() { return doghook_platform_gcc(); } -} // namespace DogHookPlatform +inline constexpr bool msvc() { return doghook_platform_msvc(); } +inline constexpr bool clang() { return doghook_platform_clang(); } +inline constexpr bool gcc() { return doghook_platform_gcc(); } +} // namespace DoghookPlatform #include "types.hh" diff --git a/src/signature.cc b/src/signature.cc index db302a9..00e7d62 100644 --- a/src/signature.cc +++ b/src/signature.cc @@ -135,7 +135,7 @@ auto Signature::resolve_library(const char *name) -> void * { auto dir = readdir(d); 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); return true; } diff --git a/src/signature.hh b/src/signature.hh index b0558f9..fde846d 100644 --- a/src/signature.hh +++ b/src/signature.hh @@ -16,37 +16,4 @@ auto find_pattern(const char *module, const char *pattern, u32 offset) { return reinterpret_cast(find_pattern(module, pattern) + offset); } 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 - auto find(u32 offset) -> T { - return ::Signature::find_pattern(module, signature, offset); - } -}; } // namespace Signature diff --git a/src/types.hh b/src/types.hh index da072cf..90d08ff 100644 --- a/src/types.hh +++ b/src/types.hh @@ -13,3 +13,5 @@ using i16 = int16_t; using i32 = int32_t; using i64 = int64_t; using iptr = intptr_t; + +#include "math.hh" diff --git a/src/vfunc.hh b/src/vfunc.hh new file mode 100644 index 0000000..0b2e757 --- /dev/null +++ b/src/vfunc.hh @@ -0,0 +1,91 @@ +#pragma once + +#include +#include + +#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(reinterpret_cast(inst) + offset); +} + +inline auto get_table(const void *inst, u32 offset) -> const void ** { + return *reinterpret_cast( + reinterpret_cast( + const_cast(inst)) + + offset); +} + +template +inline auto get_func(const void *inst, u32 index, u32 offset) { + return reinterpret_cast(get_table(inst, offset)[index]); +} + +template +inline auto get_func(void *inst, u32 index, u32 offset) { + return reinterpret_cast(get_table(inst, offset)[index]); +} + +template +class Func; + +template +class Func { + + 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( + reinterpret_cast(instance) + offset); + } else { + this->instance = instance; + } + + f = get_func(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::type; \ + using t = decltype(&c::name); \ + return VFunc::Func(this, windows, linux, osx, off).invoke(__VA_ARGS__) From 3aba3a15a91016f72618694c8cbc8d4fb951cfb3 Mon Sep 17 00:00:00 2001 From: F1ssi0N Date: Tue, 13 Mar 2018 15:48:30 +0000 Subject: [PATCH 2/2] Update interface.cc to fix linux compile issues --- src/interface.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/interface.cc b/src/interface.cc index ef1fb8f..3c96382 100644 --- a/src/interface.cc +++ b/src/interface.cc @@ -23,9 +23,7 @@ void *InterfaceHelpers::find_interface(const char *module_name, const char *inte if constexpr (DoghookPlatform::windows()) { interface_reg_head = **Signature::find_pattern(module_name, "8B 35 ? ? ? ? 57 85 F6 74 38", 2); } else if constexpr (DoghookPlatform::linux()) { - static_assert(!DoghookPlatform::linux()); } else if constexpr (DoghookPlatform::osx()) { - static_assert(!DoghookPlatform::linux()); } assert(interface_reg_head);