Skip to content

Commit

Permalink
Windows 64 bit support (#17)
Browse files Browse the repository at this point in the history
* implement windows 64 bit

* use macros for the convention source

* include fixes

* fix defines

* fix long jumping on x64

* fix the handler params

* fix the incremental no

* fix trampoline jump offset
  • Loading branch information
altalk23 authored May 26, 2024
1 parent 58dc814 commit fd1e02e
Show file tree
Hide file tree
Showing 16 changed files with 193 additions and 47 deletions.
19 changes: 14 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,17 @@ jobs:
fail-fast: false
matrix:
config:
- name: 'Windows'
id: win
- name: 'Windows (32-bit)'
id: win-32
arch: x86
os: windows-latest
build_tests: true
extra_flags: '-DCMAKE_BUILD_TYPE=Debug'
out_paths: './build/src/TulipHook.lib'

- name: 'Windows (64-bit)'
id: win-64
arch: x64
os: windows-latest
build_tests: true
extra_flags: '-DCMAKE_BUILD_TYPE=Debug'
Expand Down Expand Up @@ -65,8 +74,8 @@ jobs:
- name: Setup MSVC
uses: ilammy/[email protected]
with:
arch: x86
if: matrix.config.id == 'win'
arch: ${{ matrix.config.arch }}
if: matrix.config.id == 'win-32' || matrix.config.id == 'win-64'

# https://github.com/hendrikmuhs/ccache-action/pull/182
- name: Setup sccache
Expand All @@ -77,7 +86,7 @@ jobs:

- name: Install ninja-build tool
uses: seanmiddleditch/gha-setup-ninja@v3
if: matrix.config.id != 'win'
if: matrix.config.id != 'win-32' && matrix.config.id != 'win-64'

- name: Configure
shell: bash
Expand Down
8 changes: 6 additions & 2 deletions include/tulip/Platform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

// clang-format off

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__)

#define TULIP_HOOK_WINDOWS 1
#define TULIP_HOOK_SUPPORTED_PLATFORM 1

#define TULIP_HOOK_DEFAULT_CONV __cdecl

#define TULIP_HOOK_X86 1
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__)
#define TULIP_HOOK_X64 1
#else
#define TULIP_HOOK_X86 1
#endif

#ifdef TULIP_HOOK_DYNAMIC
#ifdef TULIP_HOOK_EXPORTING
Expand Down
4 changes: 2 additions & 2 deletions include/tulip/platform/PlatformConvention.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#include "../Platform.hpp"
#include "DefaultConvention.hpp"
#include "WindowsConvention.hpp"
#include "Windows32Convention.hpp"

namespace tulip::hook {
#if defined(TULIP_HOOK_WINDOWS)
#if defined(TULIP_HOOK_WINDOWS) && defined(TULIP_HOOK_X86)
using PlatformConvention = CdeclConvention;
#else
using PlatformConvention = DefaultConvention;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include "../Platform.hpp"

#ifdef TULIP_HOOK_WINDOWS
#if defined(TULIP_HOOK_WINDOWS) && defined(TULIP_HOOK_X86)

#include "../CallingConvention.hpp"

Expand Down
5 changes: 3 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ if(WIN32)
file(GLOB TULIP_HOOK_PLATFORM_SOURCES
assembler/X86Assembler.cpp
assembler/X64Assembler.cpp
convention/WindowsConvention.cpp
convention/Windows32Convention.cpp
generator/X86Generator.cpp
generator/X64Generator.cpp
target/WindowsTarget.cpp
target/Windows32Target.cpp
target/Windows64Target.cpp
)
elseif(APPLE)
set(TULIP_HOOK_LINK_DOBBY On)
Expand Down
2 changes: 1 addition & 1 deletion src/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Result<void*> tulip::hook::createReverseWrapper(void* address, WrapperMetadata c
std::shared_ptr<CallingConvention> tulip::hook::createConvention(TulipConvention convention) noexcept {
switch (convention) {
case TulipConvention::Default: return DefaultConvention::create();
#ifdef TULIP_HOOK_WINDOWS
#if defined(TULIP_HOOK_WINDOWS) && defined(TULIP_HOOK_X86)
case TulipConvention::Cdecl: return CdeclConvention::create();
case TulipConvention::Thiscall: return ThiscallConvention::create();
case TulipConvention::Fastcall: return FastcallConvention::create();
Expand Down
6 changes: 6 additions & 0 deletions src/assembler/X64Assembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ void X64Assembler::jmp(int64_t address) {
X86Assembler::jmp(address);
}

void X64Assembler::jmprip(int32_t offset) {
this->write8(0xff);
this->write8(0x25);
this->write32(offset);
}

void X64Assembler::call(X64Register reg) {
rex(this, reg, RAX, false);
X86Assembler::call(x86reg(reg));
Expand Down
2 changes: 2 additions & 0 deletions src/assembler/X64Assembler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ namespace tulip::hook {

void jmp(X64Register reg);
void jmp(int64_t address);
// im lazy again
void jmprip(int32_t offset);

void call(X64Register reg);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
#include <algorithm>
#include <iostream>
#include <optional>
#include <platform/WindowsConvention.hpp>
#include <platform/Windows32Convention.hpp>
#include <variant>

#if defined(TULIP_HOOK_WINDOWS) && defined(TULIP_HOOK_X86)

using namespace tulip::hook;

enum class Register {
Expand Down Expand Up @@ -685,3 +687,5 @@ std::shared_ptr<StdcallConvention> StdcallConvention::create() {
}

StdcallConvention::~StdcallConvention() {}

#endif
84 changes: 69 additions & 15 deletions src/generator/X64Generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,36 @@ std::vector<uint8_t> X64HandlerGenerator::handlerBytes(uint64_t address) {
RegMem64 m;
using enum X64Register;

#ifdef TULIP_HOOK_WINDOWS
constexpr auto SCRATCH = RAX;
constexpr auto FIRST_PARAM = RCX;
constexpr auto SECOND_PARAM = RDX;
#else
constexpr auto SCRATCH = RAX;
constexpr auto FIRST_PARAM = RDI;
constexpr auto SECOND_PARAM = RSI;
#endif

for (size_t i = 0; i < 8; ++i) {
a.nop();
}

// preserve registers
a.sub(RSP, 0xb8);
#ifdef TULIP_HOOK_WINDOWS
constexpr auto PRESERVE_SIZE = 0x78;
a.sub(RSP, PRESERVE_SIZE);

a.mov(m[RSP + 0x58], R9);
a.mov(m[RSP + 0x50], R8);
a.mov(m[RSP + 0x48], RDX);
a.mov(m[RSP + 0x40], RCX);
a.movaps(m[RSP + 0x30], XMM3);
a.movaps(m[RSP + 0x20], XMM2);
a.movaps(m[RSP + 0x10], XMM1);
a.movaps(m[RSP + 0x00], XMM0);
#else
constexpr auto PRESERVE_SIZE = 0xb8;
a.sub(RSP, PRESERVE_SIZE);

a.mov(m[RSP + 0xa8], R9);
a.mov(m[RSP + 0xa0], R8);
Expand All @@ -51,23 +75,36 @@ std::vector<uint8_t> X64HandlerGenerator::handlerBytes(uint64_t address) {
a.movaps(m[RSP + 0x20], XMM2);
a.movaps(m[RSP + 0x10], XMM1);
a.movaps(m[RSP + 0x00], XMM0);
#endif

// preserve the original return
a.mov(RAX, m[RSP + 0xb8]);
a.mov(SCRATCH, m[RSP + PRESERVE_SIZE]);

// set the new return
a.lea(RDI, "handlerCont");
a.mov(m[RSP + 0xb8], RDI);
a.lea(FIRST_PARAM, "handlerCont");
a.mov(m[RSP + PRESERVE_SIZE], FIRST_PARAM);

// set the parameters
a.mov(RDI, "content");
a.mov(RSI, RAX);
a.mov(FIRST_PARAM, "content");
a.mov(SECOND_PARAM, SCRATCH);

// call the pre handler, incrementing
a.mov(RAX, "handlerPre");
a.call(RAX);
a.mov(SCRATCH, "handlerPre");
a.call(SCRATCH);

// recover registers
#ifdef TULIP_HOOK_WINDOWS
a.movaps(XMM0, m[RSP + 0x00]);
a.movaps(XMM1, m[RSP + 0x10]);
a.movaps(XMM2, m[RSP + 0x20]);
a.movaps(XMM3, m[RSP + 0x30]);
a.mov(RCX, m[RSP + 0x40]);
a.mov(RDX, m[RSP + 0x48]);
a.mov(R8, m[RSP + 0x50]);
a.mov(R9, m[RSP + 0x58]);

a.add(RSP, PRESERVE_SIZE);
#else
a.movaps(XMM0, m[RSP + 0x00]);
a.movaps(XMM1, m[RSP + 0x10]);
a.movaps(XMM2, m[RSP + 0x20]);
Expand All @@ -83,10 +120,11 @@ std::vector<uint8_t> X64HandlerGenerator::handlerBytes(uint64_t address) {
a.mov(R8, m[RSP + 0xa0]);
a.mov(R9, m[RSP + 0xa8]);

a.add(RSP, 0xb8);
a.add(RSP, PRESERVE_SIZE);
#endif

// call the func
a.jmp(RAX);
a.jmp(SCRATCH);

a.label("handlerCont");

Expand All @@ -101,11 +139,11 @@ std::vector<uint8_t> X64HandlerGenerator::handlerBytes(uint64_t address) {
a.movaps(m[RSP + 0x00], XMM0);

// call the post handler, decrementing
a.mov(RAX, "handlerPost");
a.call(RAX);
a.mov(SCRATCH, "handlerPost");
a.call(SCRATCH);

// recover the original return
a.mov(m[RSP + 0x38], RAX);
a.mov(m[RSP + 0x38], SCRATCH);

// recover the return values
a.movaps(XMM0, m[RSP + 0x00]);
Expand Down Expand Up @@ -137,7 +175,15 @@ std::vector<uint8_t> X64HandlerGenerator::intervenerBytes(uint64_t address) {
RegMem64 m;
using enum X64Register;

a.jmp(reinterpret_cast<uint64_t>(m_handler));
auto difference = reinterpret_cast<int64_t>(m_handler) - static_cast<int64_t>(address) - 5;

if (difference <= 0x7fffffff && difference >= -0x80000000) {
a.jmp(reinterpret_cast<uint64_t>(m_handler));
}
else {
a.jmprip(0);
a.write64(reinterpret_cast<uint64_t>(m_handler));
}

return std::move(a.m_buffer);
}
Expand All @@ -147,7 +193,15 @@ std::vector<uint8_t> X64HandlerGenerator::trampolineBytes(uint64_t address, size
RegMem64 m;
using enum X64Register;

a.jmp(reinterpret_cast<uint64_t>(m_address) + offset);
auto difference = reinterpret_cast<int64_t>(m_address) - static_cast<int64_t>(address) - 5 + offset;

if (difference <= 0x7fffffff && difference >= -0x80000000) {
a.jmp(reinterpret_cast<uint64_t>(m_address) + offset);
}
else {
a.jmprip(0);
a.write64(reinterpret_cast<uint64_t>(m_address) + offset);
}

return std::move(a.m_buffer);
}
2 changes: 1 addition & 1 deletion src/target/PlatformTarget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
#include "MacosM1Target.hpp"
#include "PosixArmV7Target.hpp"
#include "PosixArmV8Target.hpp"
#include "WindowsTarget.hpp"
#include "Windows32Target.hpp"
32 changes: 18 additions & 14 deletions src/target/WindowsTarget.cpp → src/target/Windows32Target.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "WindowsTarget.hpp"
#include "Windows32Target.hpp"

#include <Platform.hpp>
#include <stdexcept>
Expand All @@ -7,10 +7,19 @@ using namespace tulip::hook;

#if defined(TULIP_HOOK_WINDOWS)

#if defined(TULIP_HOOK_X86)

Target& Target::get() {
static Windows32Target ret;
return ret;
}

#endif

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

Result<> WindowsTarget::allocatePage() {
Result<> Windows32Target::allocatePage() {
m_allocatedPage = VirtualAlloc(nullptr, 0x4000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READ);

if (!m_allocatedPage) {
Expand All @@ -23,7 +32,7 @@ Result<> WindowsTarget::allocatePage() {
return Ok();
}

Result<uint32_t> WindowsTarget::getProtection(void* address) {
Result<uint32_t> Windows32Target::getProtection(void* address) {
MEMORY_BASIC_INFORMATION information;

if (!VirtualQuery(address, &information, sizeof(MEMORY_BASIC_INFORMATION))) {
Expand All @@ -33,7 +42,7 @@ Result<uint32_t> WindowsTarget::getProtection(void* address) {
return Ok(information.Protect);
}

Result<> WindowsTarget::protectMemory(void* address, size_t size, uint32_t protection) {
Result<> Windows32Target::protectMemory(void* address, size_t size, uint32_t protection) {
DWORD oldProtection;

if (!VirtualProtect(address, size, protection, &oldProtection)) {
Expand All @@ -43,23 +52,18 @@ Result<> WindowsTarget::protectMemory(void* address, size_t size, uint32_t prote
return Ok();
}

Result<> WindowsTarget::rawWriteMemory(void* destination, void const* source, size_t size) {
Result<> Windows32Target::rawWriteMemory(void* destination, void const* source, size_t size) {
if (!WriteProcessMemory(GetCurrentProcess(), destination, source, size, nullptr)) {
return Err("Unable to write to memory");
}
return Ok();
}

uint32_t WindowsTarget::getWritableProtection() {
uint32_t Windows32Target::getWritableProtection() {
return PAGE_READWRITE;
}

Target& Target::get() {
static WindowsTarget ret;
return ret;
}

Result<csh> WindowsTarget::openCapstone() {
Result<csh> Windows32Target::openCapstone() {
cs_err status;

status = cs_open(CS_ARCH_X86, CS_MODE_32, &m_capstone);
Expand All @@ -70,13 +74,13 @@ Result<csh> WindowsTarget::openCapstone() {
return Ok(m_capstone);
}

std::unique_ptr<HandlerGenerator> WindowsTarget::getHandlerGenerator(
std::unique_ptr<HandlerGenerator> Windows32Target::getHandlerGenerator(
void* address, void* trampoline, void* handler, void* content, void* wrapped, HandlerMetadata const& metadata
) {
return std::make_unique<X86HandlerGenerator>(address, trampoline, handler, content, wrapped, metadata);
}

std::unique_ptr<WrapperGenerator> WindowsTarget::getWrapperGenerator(void* address, WrapperMetadata const& metadata) {
std::unique_ptr<WrapperGenerator> Windows32Target::getWrapperGenerator(void* address, WrapperMetadata const& metadata) {
return std::make_unique<X86WrapperGenerator>(address, metadata);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "Target.hpp"

namespace tulip::hook {
class WindowsTarget : public Target {
class Windows32Target : public Target {
public:
using Target::Target;

Expand Down
Loading

0 comments on commit fd1e02e

Please sign in to comment.