diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ca10098..d14b0c6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,9 @@ add_library(TulipHook STATIC platform/WindowsGenerator.cpp platform/WindowsTarget.cpp platform/X86Generator.cpp + assembler/BaseAssembler.cpp + assembler/x86Assembler.cpp + assembler/x64Assembler.cpp ) target_compile_features(TulipHook PUBLIC cxx_std_20) diff --git a/src/assembler/BaseAssembler.cpp b/src/assembler/BaseAssembler.cpp new file mode 100644 index 0000000..5256509 --- /dev/null +++ b/src/assembler/BaseAssembler.cpp @@ -0,0 +1,56 @@ +#include "BaseAssembler.hpp" + +using namespace tulip::hook; + +BaseAssembler::BaseAssembler(uint64_t baseAddress) : + m_baseAddress(baseAddress) {} + +BaseAssembler::~BaseAssembler() {} + +void BaseAssembler::write8(uint8_t value) { + m_buffer.push_back(value); +} + +void BaseAssembler::write16(uint16_t value) { + write8(value & 0xFF); + write8((value >> 8) & 0xFF); +} + +void BaseAssembler::write32(uint32_t value) { + write16(value & 0xFFFF); + write16((value >> 16) & 0xFFFF); +} + +void BaseAssembler::write64(uint64_t value) { + write32(value & 0xFFFFFFFF); + write32((value >> 32) & 0xFFFFFFFF); +} + +uint64_t BaseAssembler::currentAddress() const { + return m_baseAddress + m_buffer.size(); +} + +void BaseAssembler::rewrite8(uint64_t address, uint8_t value) { + m_buffer[address - m_baseAddress] = value; +} + +void BaseAssembler::rewrite16(uint64_t address, uint16_t value) { + rewrite8(address, value & 0xFF); + rewrite8(address + 1, (value >> 8) & 0xFF); +} + +void BaseAssembler::rewrite32(uint64_t address, uint32_t value) { + rewrite16(address, value & 0xFFFF); + rewrite16(address + 2, (value >> 16) & 0xFFFF); +} + +void BaseAssembler::rewrite64(uint64_t address, uint64_t value) { + rewrite32(address, value & 0xFFFFFFFF); + rewrite32(address + 4, (value >> 32) & 0xFFFFFFFF); +} + +void BaseAssembler::label(std::string const& name) { + m_labels[name] = this->currentAddress(); +} + +void BaseAssembler::updateLabels() {} \ No newline at end of file diff --git a/src/assembler/BaseAssembler.hpp b/src/assembler/BaseAssembler.hpp new file mode 100644 index 0000000..b4f462b --- /dev/null +++ b/src/assembler/BaseAssembler.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include + +namespace tulip::hook { + + struct AssemblerLabelUpdates { + uint64_t m_address; + std::string m_name; + uint8_t m_size; + }; + + class BaseAssembler { + public: + uint64_t m_baseAddress; + std::vector m_buffer; + std::unordered_map m_labels; + std::vector m_labelUpdates; + + BaseAssembler(uint64_t baseAddress); + BaseAssembler(BaseAssembler const&) = delete; + BaseAssembler(BaseAssembler&&) = delete; + ~BaseAssembler(); + + uint64_t currentAddress() const; + + void write8(uint8_t value); + void write16(uint16_t value); + void write32(uint32_t value); + void write64(uint64_t value); + + void rewrite8(uint64_t address, uint8_t value); + void rewrite16(uint64_t address, uint16_t value); + void rewrite32(uint64_t address, uint32_t value); + void rewrite64(uint64_t address, uint64_t value); + + void label(std::string const& name); + + virtual void updateLabels(); + }; +} \ No newline at end of file diff --git a/src/assembler/X64Assembler.cpp b/src/assembler/X64Assembler.cpp new file mode 100644 index 0000000..2e14423 --- /dev/null +++ b/src/assembler/X64Assembler.cpp @@ -0,0 +1,163 @@ +#include "X64Assembler.hpp" + +using namespace tulip::hook; + +uint8_t regv(X64Register reg) { + return static_cast(reg); +} + +uint8_t regv(X64Pointer ptr) { + return regv(ptr.m_register); +} + +uint8_t regl(X64Register reg) { + return regv(reg) & 0x7; +} + +uint8_t regl(X64Pointer ptr) { + return regv(ptr) & 0x7; +} + +uint8_t regx(X64Register reg) { + return regv(reg) - 0x80; +} + +bool espcheck(X64Pointer ptr) { + return regl(ptr) == 0x4; +} + +bool lowerreg(X64Register reg) { + return regv(reg) < 0x8; +} + +bool lowerreg(X64Pointer ptr) { + return regv(ptr) < 0x8; +} + +uint8_t lowerv(X64Register reg, uint8_t offset) { + return lowerreg(reg) << offset; +} + +uint8_t lowerv(X64Pointer ptr, uint8_t offset) { + return lowerreg(ptr) << offset; +} + +X64Assembler::X64Assembler(uint64_t baseAddress) : + BaseAssembler(baseAddress) {} + +X64Assembler::~X64Assembler() {} + +void X64Assembler::label32(std::string const& name) { + m_labelUpdates.push_back({this->currentAddress(), name, 4}); + this->write32(0); +} + +void X64Assembler::updateLabels() { + for (auto const& update : m_labelUpdates) { + this->rewrite32(update.m_address, m_labels[update.m_name] - update.m_address - 4); + } +} + +void X64Assembler::nop() { + this->write8(0x90); +} + +void X64Assembler::add(X64Register reg, uint32_t value) { + this->write8(0x48 | lowerv(reg, 0)); + this->write8(0x81); + this->write8(0xC0 | regl(reg)); + this->write32(value); +} + +void X64Assembler::sub(X64Register reg, uint32_t value) { + this->write8(0x48 | lowerv(reg, 0)); + this->write8(0x81); + this->write8(0xE8 | regl(reg)); + this->write32(value); +} + +void X64Assembler::jmp(X64Register reg) { + this->write8(0x40 | lowerv(reg, 0)); + this->write8(0xFF); + this->write8(0xE0 | regl(reg)); +} + +void X64Assembler::jmp(uint64_t address) { + this->write8(0xE9); + this->write32(address - this->currentAddress() - 4); +} + +void X64Assembler::call(X64Register reg) { + this->write8(0x40 | lowerv(reg, 0)); + this->write8(0xFF); + this->write8(0xD0 | regl(reg)); +} + +void X64Assembler::lea(X64Register reg, std::string const& label) { + this->write8(0x48 | lowerv(reg, 2)); + this->write8(0x8D); + this->write8(0x05 | regl(reg) * 8); + this->label32(label); +} + +void X64Assembler::movaps(X64Register reg, X64Pointer ptr) { + this->write8(0x40 | lowerv(ptr, 0)); + this->write8(0x0F); + this->write8(0x28); + this->write8(0x80 | regl(ptr) | regx(reg) * 8); + if (espcheck(ptr)) { + this->write8(0x24); + } + this->write32(ptr.m_offset); +} + +void X64Assembler::movaps(X64Pointer ptr, X64Register reg) { + this->write8(0x40 | lowerv(ptr, 0)); + this->write8(0x0F); + this->write8(0x29); + this->write8(0x80 | regl(ptr) | regx(reg) * 8); + if (espcheck(ptr)) { + this->write8(0x24); + } + this->write32(ptr.m_offset); +} + +void X64Assembler::mov(X64Register reg, uint32_t value) { + this->write8(0x48 | lowerv(reg, 0)); + this->write8(0xC7); + this->write8(0xC0 | regl(reg)); + this->write32(value); +} + +void X64Assembler::mov(X64Register reg, X64Pointer ptr) { + this->write8(0x48 | lowerv(ptr, 0) | lowerv(reg, 2)); + this->write8(0x8B); + this->write8(0x80 | regl(ptr) | regl(reg) * 8); + if (espcheck(ptr)) { + this->write8(0x24); + } + this->write32(ptr.m_offset); +} + +void X64Assembler::mov(X64Pointer ptr, X64Register reg) { + this->write8(0x48 | lowerv(ptr, 0) | lowerv(reg, 2)); + this->write8(0x89); + this->write8(0x80 | regl(ptr) | regl(reg) * 8); + if (espcheck(ptr)) { + this->write8(0x24); + } + this->write32(ptr.m_offset); +} + +void X64Assembler::mov(X64Register reg, X64Register reg2) { + this->write8(0x48 | lowerv(reg, 0) | lowerv(reg2, 2)); + this->write8(0x89); + this->write8(0xC0 | regl(reg) | regl(reg2) * 8); +} + +void X64Assembler::mov(X64Register reg, std::string const& label) { + this->write8(0x48 | lowerv(reg, 2)); + this->write8(0x8B); + this->write8(0x05 | regl(reg) * 8); + this->label32(label); +} \ No newline at end of file diff --git a/src/assembler/X64Assembler.hpp b/src/assembler/X64Assembler.hpp new file mode 100644 index 0000000..0cc802c --- /dev/null +++ b/src/assembler/X64Assembler.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include "BaseAssembler.hpp" + +namespace tulip::hook { + + enum class X64Register : uint8_t { + RAX = 0x0, + RCX, + RDX, + RBX, + RSP, + RBP, + RSI, + RDI, + R8, + R9, + R10, + R11, + R12, + R13, + R14, + R15, + RIP = 0x40, + XMM0 = 0x80, + XMM1, + XMM2, + XMM3, + XMM4, + XMM5, + XMM6, + XMM7 + }; + + struct X64Pointer { + X64Register m_register; + int32_t m_offset = 0; + }; + + class X64Assembler : public BaseAssembler { + public: + X64Assembler(uint64_t baseAddress); + X64Assembler(X64Assembler const&) = delete; + X64Assembler(X64Assembler&&) = delete; + ~X64Assembler(); + + void label32(std::string const& name); + void updateLabels() override; + + void nop(); + + void add(X64Register reg, uint32_t value); + void sub(X64Register reg, uint32_t value); + + void jmp(X64Register reg); + void jmp(uint64_t address); + + void call(X64Register reg); + + void lea(X64Register reg, std::string const& label); + + void movaps(X64Register reg, X64Pointer ptr); + void movaps(X64Pointer ptr, X64Register reg); + + void mov(X64Register reg, uint32_t value); + void mov(X64Register reg, X64Pointer ptr); + void mov(X64Pointer ptr, X64Register reg); + void mov(X64Register reg, X64Register reg2); + void mov(X64Register reg, std::string const& label); + }; +} \ No newline at end of file diff --git a/src/assembler/X86Assembler.cpp b/src/assembler/X86Assembler.cpp new file mode 100644 index 0000000..2fd0ad3 --- /dev/null +++ b/src/assembler/X86Assembler.cpp @@ -0,0 +1,139 @@ +#include "X86Assembler.hpp" + +using namespace tulip::hook; + +uint8_t regv(X86Register reg) { + return static_cast(reg); +} + +uint8_t regv(X86Pointer ptr) { + return regv(ptr.m_register); +} + +uint8_t regx(X86Register reg) { + return regv(reg) - 0x80; +} + +bool espcheck(X86Pointer ptr) { + return regv(ptr) == 0x4; +} + +X86Assembler::X86Assembler(uint64_t baseAddress) : + BaseAssembler(baseAddress) {} + +X86Assembler::~X86Assembler() {} + +void X86Assembler::label32(std::string const& name) { + m_labelUpdates.push_back({this->currentAddress(), name, 4}); + this->write32(0); +} + +void X86Assembler::updateLabels() { + for (auto const& update : m_labelUpdates) { + this->rewrite32(update.m_address, m_labels[update.m_name] - update.m_address - 4); + } +} + +void X86Assembler::nop() { + this->write8(0x90); +} + +void X86Assembler::add(X86Register reg, uint32_t value) { + this->write8(0x81); + this->write8(0xC0 | regv(reg)); + this->write32(value); +} + +void X86Assembler::sub(X86Register reg, uint32_t value) { + this->write8(0x81); + this->write8(0xE8 | regv(reg)); + this->write32(value); +} + +void X86Assembler::push(X86Register reg) { + this->write8(0x50 | regv(reg)); +} + +void X86Assembler::push(X86Pointer reg) { + this->write8(0xFF); + this->write8(0xB0 | regv(reg)); + if (espcheck(reg)) { + this->write8(0x24); + } + this->write32(reg.m_offset); +} + +void X86Assembler::pop(X86Register reg) { + this->write8(0x58 | regv(reg)); +} + +void X86Assembler::jmp(X86Register reg) { + this->write8(0xFF); + this->write8(0xE0 | regv(reg)); +} + +void X86Assembler::jmp(uint64_t address) { + this->write8(0xE9); + this->write32(address - this->currentAddress() - 4); +} + +void X86Assembler::call(X86Register reg) { + this->write8(0xFF); + this->write8(0xD0 | regv(reg)); +} + +void X86Assembler::movsd(X86Register reg, X86Pointer ptr) { + this->write8(0xF2); + this->write8(0x0F); + this->write8(0x10); + this->write8(0x80 | regv(ptr) | regx(reg) * 8); + if (espcheck(ptr)) { + this->write8(0x24); + } + this->write32(ptr.m_offset); +} + +void X86Assembler::movsd(X86Pointer ptr, X86Register reg) { + this->write8(0xF2); + this->write8(0x0F); + this->write8(0x11); + this->write8(0x80 | regv(ptr) | regx(reg) * 8); + if (espcheck(ptr)) { + this->write8(0x24); + } + this->write32(ptr.m_offset); +} + +void X86Assembler::mov(X86Register reg, uint32_t value) { + this->write8(0xB8 | regv(reg)); + this->write32(value); +} + +void X86Assembler::mov(X86Register reg, X86Pointer ptr) { + this->write8(0x8B); + this->write8(0x80 | regv(ptr) | regx(reg) * 8); + if (espcheck(ptr)) { + this->write8(0x24); + } + this->write32(ptr.m_offset); +} + +void X86Assembler::mov(X86Pointer ptr, X86Register reg) { + this->write8(0x89); + this->write8(0x80 | regv(ptr) | regx(reg) * 8); + if (espcheck(ptr)) { + this->write8(0x24); + } + this->write32(ptr.m_offset); +} + +void X86Assembler::mov(X86Register reg, X86Register reg2) { + this->write8(0x89); + this->write8(0xC0 | regv(reg) | regx(reg2) * 8); +} + +void X86Assembler::mov(X86Register reg, std::string const& label) { + this->write8(0x8b); + this->write8(0x05 | regv(reg) * 8); + this->label32(label); +} \ No newline at end of file diff --git a/src/assembler/X86Assembler.hpp b/src/assembler/X86Assembler.hpp new file mode 100644 index 0000000..6b47dd3 --- /dev/null +++ b/src/assembler/X86Assembler.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "BaseAssembler.hpp" + +namespace tulip::hook { + + enum class X86Register : uint8_t { + EAX = 0x0, + ECX, + EDX, + EBX, + ESP, + EBP, + ESI, + EDI, + EIP = 0x40, + XMM0 = 0x80, + XMM1, + XMM2, + XMM3, + XMM4, + XMM5, + XMM6, + XMM7 + }; + + struct X86Pointer { + X86Register m_register; + int32_t m_offset = 0; + }; + + class X86Assembler : public BaseAssembler { + public: + X86Assembler(uint64_t baseAddress); + X86Assembler(X86Assembler const&) = delete; + X86Assembler(X86Assembler&&) = delete; + ~X86Assembler(); + + void label32(std::string const& name); + void updateLabels() override; + + void nop(); + + void add(X86Register reg, uint32_t value); + void sub(X86Register reg, uint32_t value); + + void push(X86Register reg); + void push(X86Pointer reg); + void pop(X86Register reg); + + void jmp(X86Register reg); + void jmp(uint64_t address); + + void call(X86Register reg); + + void movsd(X86Register reg, X86Pointer ptr); + void movsd(X86Pointer ptr, X86Register reg); + + void mov(X86Register reg, uint32_t value); + void mov(X86Register reg, X86Pointer ptr); + void mov(X86Pointer ptr, X86Register reg); + void mov(X86Register reg, X86Register reg2); + void mov(X86Register reg, std::string const& label); + }; +} \ No newline at end of file