Merge pull request from geode-sdk/win-64

Win 64 support
This commit is contained in:
mat 2024-06-01 17:12:30 -03:00 committed by GitHub
commit ccfa6edf53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 219 additions and 29 deletions

View file

@ -243,7 +243,7 @@ if (DEFINED GEODE_TULIPHOOK_REPO_PATH)
message(STATUS "Using ${GEODE_TULIPHOOK_REPO_PATH} for TulipHook")
add_subdirectory(${GEODE_TULIPHOOK_REPO_PATH} ${GEODE_TULIPHOOK_REPO_PATH}/build)
else()
CPMAddPackage("gh:geode-sdk/TulipHook#58dc814")
CPMAddPackage("gh:geode-sdk/TulipHook#fd1e02e")
endif()
set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE)

View file

@ -9,18 +9,30 @@
// Set dllexport/dllimport to geode classes & functions
#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 GEODE_WINDOWS(...) __VA_ARGS__
#define GEODE_IS_WINDOWS
#define GEODE_IS_DESKTOP
#define GEODE_PLATFORM_NAME "Windows"
#define GEODE_CALL __stdcall
#define GEODE_CDECL_CALL __cdecl
#define GEODE_PLATFORM_EXTENSION ".dll"
#define GEODE_PLATFORM_SHORT_IDENTIFIER "win"
#define CC_TARGET_OS_WIN32
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__)
#define GEODE_IS_WINDOWS64
#define GEODE_WINDOWS64(...) __VA_ARGS__
#define GEODE_WINDOWS32(...)
#define GEODE_CALL
#else
#define GEODE_IS_WINDOWS32
#define GEODE_WINDOWS32(...) __VA_ARGS__
#define GEODE_WINDOWS64(...)
#define GEODE_CALL __stdcall
#endif
#else
#define GEODE_WINDOWS(...)
#define GEODE_WINDOWS32(...)
#define GEODE_WINDOWS64(...)
#endif
#if defined(__APPLE__)
@ -45,7 +57,6 @@
#define CC_TARGET_OS_MAC
#endif
#define GEODE_CALL
#define GEODE_CDECL_CALL
#else
#define GEODE_MACOS(...)
#define GEODE_IOS(...)
@ -57,7 +68,6 @@
#define GEODE_IS_ANDROID
#define GEODE_IS_MOBILE
#define GEODE_CALL
#define GEODE_CDECL_CALL
#define CC_TARGET_OS_ANDROID
#if defined(__arm__)

View file

@ -27,7 +27,13 @@
#define GEODE_API extern "C" __declspec(dllexport)
#define GEODE_EXPORT __declspec(dllexport)
static_assert(sizeof(void*) == 4, "Geode must be compiled in 32-bit for Windows!");
#if defined(GEODE_IS_WINDOWS64)
#define GEODE_IS_X64
#define GEODE_CDECL_CALL
#else
#define GEODE_IS_X86
#define GEODE_CDECL_CALL __cdecl
#endif
#include "windows.hpp"
@ -47,6 +53,9 @@
#define GEODE_API extern "C" __attribute__((visibility("default")))
#define GEODE_EXPORT __attribute__((visibility("default")))
#define GEODE_IS_X64
#define GEODE_CDECL_CALL
#include "macos.hpp"
#elif defined(GEODE_IS_IOS)
@ -65,6 +74,9 @@
#define GEODE_API extern "C" __attribute__((visibility("default")))
#define GEODE_EXPORT __attribute__((visibility("default")))
#define GEODE_IS_X64
#define GEODE_CDECL_CALL
#include "ios.hpp"
#elif defined(GEODE_IS_ANDROID)
@ -83,6 +95,13 @@
#define GEODE_API extern "C" __attribute__((visibility("default")))
#define GEODE_EXPORT __attribute__((visibility("default")))
#if defined(GEODE_IS_ANDROID64)
#define GEODE_IS_X64
#else
#define GEODE_IS_X86
#endif
#define GEODE_CDECL_CALL
#include "android.hpp"
#else

View file

@ -27,36 +27,53 @@ namespace geode::base {
}
}
namespace geode::cast {
template <class Type>
struct ShrunkPointer {
uint32_t m_ptrOffset;
Type* into(uintptr_t base) {
return reinterpret_cast<Type*>(base + m_ptrOffset);
}
};
struct TypeDescriptorType {
void* m_typeinfoTable;
int32_t m_spare;
intptr_t m_spare;
char m_typeDescriptorName[0x100];
};
struct ClassDescriptorType;
struct BaseClassDescriptorType {
TypeDescriptorType* m_typeDescriptor;
ShrunkPointer<TypeDescriptorType> m_typeDescriptor;
int32_t m_numContainedBases;
int32_t m_memberDisplacement[3];
int32_t m_attributes;
ClassDescriptorType* m_classDescriptor;
ShrunkPointer<ClassDescriptorType> m_classDescriptor;
};
struct BaseClassArrayType {
ShrunkPointer<BaseClassDescriptorType> m_descriptorEntries[0x100];
};
struct ClassDescriptorType {
int32_t m_signature;
int32_t m_attributes;
int32_t m_numBaseClasses;
BaseClassDescriptorType** m_baseClassArray;
ShrunkPointer<BaseClassArrayType> m_baseClassArray;
};
struct CompleteLocatorType {
int32_t m_signature;
int32_t m_offset;
int32_t m_cdOffset;
TypeDescriptorType* m_typeDescriptor;
ClassDescriptorType* m_classDescriptor;
ShrunkPointer<TypeDescriptorType> m_typeDescriptor;
ShrunkPointer<ClassDescriptorType> m_classDescriptor;
#ifdef GEODE_IS_X64
int32_t m_locatorOffset;
#endif
};
struct MetaPointerType {
@ -89,12 +106,20 @@ namespace geode::cast {
auto afterIdent = static_cast<char const*>(afterDesc->m_typeDescriptorName);
auto classDesc = metaPtr->m_completeLocator->m_classDescriptor;
#ifdef GEODE_IS_X64
auto locatorOffset = metaPtr->m_completeLocator->m_locatorOffset;
auto base = reinterpret_cast<uintptr_t>(metaPtr->m_completeLocator) - locatorOffset;
#else
auto base = 0;
#endif
auto classDesc = metaPtr->m_completeLocator->m_classDescriptor.into(base);
for (int32_t i = 0; i < classDesc->m_numBaseClasses; ++i) {
auto entry = classDesc->m_baseClassArray.into(base)->m_descriptorEntries[i].into(base);
auto optionIdent = static_cast<char const*>(
classDesc->m_baseClassArray[i]->m_typeDescriptor->m_typeDescriptorName
entry->m_typeDescriptor.into(base)->m_typeDescriptorName
);
auto optionOffset = classDesc->m_baseClassArray[i]->m_memberDisplacement[0];
auto optionOffset = entry->m_memberDisplacement[0];
if (std::strcmp(afterIdent, optionIdent) == 0) {
auto afterPtr = reinterpret_cast<std::byte*>(basePtr) + optionOffset;

View file

@ -42,10 +42,19 @@ DWORD WINAPI xinputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRen
return getDSoundAudioDeviceGuids(dwUserIndex, pDSoundRenderGuid, pDSoundCaptureGuid);
}
#pragma comment(linker, "/export:XInputGetState=_xinputGetState@8")
#pragma comment(linker, "/export:XInputSetState=_xinputSetState@8")
#pragma comment(linker, "/export:XInputGetCapabilities=_xinputGetCapabilities@12")
#pragma comment(linker, "/export:XInputGetDSoundAudioDeviceGuids=_xinputGetDSoundAudioDeviceGuids@12")
// https://github.com/mrexodia/perfect-dll-proxy
#if defined(_WIN64)
#define PROXY_PATH(export) \
"/export:" #export "=\\\\.\\GLOBALROOT\\SystemRoot\\System32\\XInput9_1_0.dll." #export
#else
#define PROXY_PATH(export) \
"/export:" #export "=\\\\.\\GLOBALROOT\\SystemRoot\\SysWOW64\\XInput9_1_0.dll." #export
#endif
#pragma comment(linker, PROXY_PATH(XInputGetState))
#pragma comment(linker, PROXY_PATH(XInputSetState))
#pragma comment(linker, PROXY_PATH(XInputGetCapabilities))
#pragma comment(linker, PROXY_PATH(XInputGetDSoundAudioDeviceGuids))
BOOL fileExists(char const* path) {
DWORD attrib = GetFileAttributesA(path);

View file

@ -383,7 +383,7 @@ int ZipUtils::ccInflateCCZFile(char const* path, unsigned char** out) {
}
unsigned long destlen = len;
unsigned long source = (unsigned long)compressed + sizeof(*header);
uintptr_t source = (uintptr_t)compressed + sizeof(*header);
int ret = uncompress(*out, &destlen, (Bytef*)source, fileLen - sizeof(*header));
delete[] compressed;

View file

@ -28,14 +28,14 @@ $execute {
reinterpret_cast<void*>(geode::addresser::getNonVirtual(&FMOD::System::init)),
&FMOD_System_init_hook,
"FMOD::System::init"
GEODE_WINDOWS(, tulip::hook::TulipConvention::Stdcall)
GEODE_WINDOWS32(, tulip::hook::TulipConvention::Stdcall)
);
(void)geode::Mod::get()->hook(
reinterpret_cast<void*>(geode::addresser::getNonVirtual(&FMOD::ChannelControl::setVolume)),
&FMOD_ChannelControl_setVolume_hook,
"FMOD::ChannelControl::setVolume"
GEODE_WINDOWS(, tulip::hook::TulipConvention::Stdcall)
GEODE_WINDOWS32(, tulip::hook::TulipConvention::Stdcall)
);
}

View file

@ -38,6 +38,7 @@ $execute {
// geode::base::getCocos() + 0x1225DC = MessageBoxW in .idata
if (LoaderImpl::get()->isForwardCompatMode()) return;
#if GEODE_COMP_GD_VERSION == 22040
const uint32_t importedMessageBoxA = geode::base::getCocos() + 0x122600;
ByteVector p = {
@ -48,6 +49,9 @@ $execute {
(void)Mod::get()->patch(reinterpret_cast<void*>(geode::base::getCocos() + 0xC75F9), p);
(void)Mod::get()->patch(reinterpret_cast<void*>(geode::base::getCocos() + 0xc7651), p);
#else
#pragma message("Unsupported GD version!")
#endif
}
#endif

View file

@ -38,11 +38,15 @@ $execute {
if (LoaderImpl::get()->isForwardCompatMode()) return;
// BitmapDC::~BitmapDC
#if GEODE_COMP_GD_VERSION == 22040
patchCall(0xC9A56, (uintptr_t)&RemoveFontResourceWHook);
// BitmapDC::setFont
patchCall(0xCB5BC, (uintptr_t)&RemoveFontResourceWHook);
patchCall(0xCB642, (uintptr_t)&AddFontResourceWHook);
#else
#pragma message("Unsupported GD version!")
#endif
};
#endif

View file

@ -151,11 +151,18 @@ static std::string getStacktrace(PCONTEXT context) {
auto process = GetCurrentProcess();
auto thread = GetCurrentThread();
#ifdef GEODE_IS_X86
stack.AddrPC.Offset = context->Eip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = context->Esp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = context->Ebp;
#else
stack.AddrPC.Offset = context->Rip;
stack.AddrStack.Offset = context->Rsp;
stack.AddrFrame.Offset = context->Rbp;
#endif
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Mode = AddrModeFlat;
// size_t frame = 0;
@ -174,6 +181,7 @@ static std::string getStacktrace(PCONTEXT context) {
}
static std::string getRegisters(PCONTEXT context) {
#ifdef GEODE_IS_X86
return fmt::format(
"EAX: {:08x}\n"
"EBX: {:08x}\n"
@ -194,6 +202,44 @@ static std::string getRegisters(PCONTEXT context) {
context->Esi,
context->Eip
);
#else
return fmt::format(
"RAX: {:016x}\n"
"RBX: {:016x}\n"
"RCX: {:016x}\n"
"RDX: {:016x}\n"
"RBP: {:016x}\n"
"RSP: {:016x}\n"
"RDI: {:016x}\n"
"RSI: {:016x}\n"
"RIP: {:016x}\n"
"R8: {:016x}\n"
"R9: {:016x}\n"
"R10: {:016x}\n"
"R11: {:016x}\n"
"R12: {:016x}\n"
"R13: {:016x}\n"
"R14: {:016x}\n"
"R15: {:016x}\n",
context->Rax,
context->Rbx,
context->Rcx,
context->Rdx,
context->Rbp,
context->Rsp,
context->Rdi,
context->Rsi,
context->Rip,
context->R8,
context->R9,
context->R10,
context->R11,
context->R12,
context->R13,
context->R14,
context->R15
);
#endif
}
template <typename T, typename U>

View file

@ -65,6 +65,8 @@ std::string loadGeode() {
gdTimestamp = ntHeader->FileHeader.TimeDateStamp;
#ifdef GEODE_IS_WINDOWS32
constexpr size_t trampolineSize = 12;
mainTrampolineAddr = VirtualAlloc(
nullptr, trampolineSize,
@ -128,6 +130,9 @@ std::string loadGeode() {
return "Geode could not hook the main function, not loading Geode.";
std::memcpy(reinterpret_cast<void*>(patchAddr), patchBytes, patchSize);
VirtualProtectEx(process, reinterpret_cast<void*>(patchAddr), patchSize, oldProtect, &oldProtect);
#else
#pragma message("64-bit entry is not implemented yet.")
#endif
return "";
}

View file

@ -69,12 +69,26 @@ Addresser::MultipleInheritance* Addresser::instance() {
#ifdef GEODE_IS_WINDOWS
#include <delayimp.h>
extern "C" FARPROC WINAPI __delayLoadHelper2(PCImgDelayDescr pidd, FARPROC* ppfnIATEntry); // NOLINT(*-reserved-identifier)
FARPROC WINAPI delayLoadHook(unsigned dliNotify, PDelayLoadInfo pdli) {
switch (dliNotify) {
case dliFailLoadLib:
case dliFailGetProc:
// incase the delayload helper fails at all (missing symbol, or library entirely),
// return -1, so we can more easily handle it below
return (FARPROC)(-1);
default:
return NULL;
}
}
extern "C" const PfnDliHook __pfnDliFailureHook2 = delayLoadHook;
#endif
intptr_t Addresser::followThunkFunction(intptr_t address) {
#ifdef GEODE_IS_WINDOWS
#ifdef GEODE_IS_WINDOWS32
// if theres a jmp at the start
if (*reinterpret_cast<uint8_t*>(address) == 0xE9) {
if (address && *reinterpret_cast<uint8_t*>(address) == 0xE9) {
auto relative = *reinterpret_cast<uint32_t*>(address + 1);
auto newAddress = address + relative + 5;
// and if that jmp leads to a jmp dword ptr, only then follow it,
@ -87,7 +101,7 @@ intptr_t Addresser::followThunkFunction(intptr_t address) {
}
// check if first instruction is a jmp dword ptr [....], i.e. if the func is a thunk
if (*reinterpret_cast<uint8_t*>(address) == 0xFF && *reinterpret_cast<uint8_t*>(address + 1) == 0x25) {
if (address && *reinterpret_cast<uint8_t*>(address) == 0xFF && *reinterpret_cast<uint8_t*>(address + 1) == 0x25) {
// read where the jmp reads from
address = *reinterpret_cast<uint32_t*>(address + 2);
// that then contains the actual address of the func
@ -95,7 +109,7 @@ intptr_t Addresser::followThunkFunction(intptr_t address) {
}
// if it starts with mov eax,..., it's a delay loaded func
if (*reinterpret_cast<uint8_t*>(address) == 0xB8) {
if (address && *reinterpret_cast<uint8_t*>(address) == 0xB8) {
// follow the jmp to the tailMerge func and grab the ImgDelayDescr pointer from there
// do it this way instead of grabbing it from the NT header ourselves because
// we don't know the dll name
@ -110,6 +124,60 @@ intptr_t Addresser::followThunkFunction(intptr_t address) {
// get the address of the function, loading the library if needed
address = reinterpret_cast<intptr_t>(__delayLoadHelper2(idd, imp));
// if the helper failed, it will return -1, so we can handle it here
if (address == -1) {
address = 0;
}
}
#endif
#ifdef GEODE_IS_WINDOWS64
static constexpr auto checkByteSequence = [](uintptr_t address, const std::initializer_list<uint8_t>& bytes) {
for (auto byte : bytes) {
if (*reinterpret_cast<uint8_t*>(address++) != byte) {
return false;
}
}
return true;
};
// check if first instruction is a jmp qword ptr [rip + ...], i.e. if the func is a thunk
// FF 25 xxxxxxxx
if (address && checkByteSequence(address, {0xFF, 0x25})) {
const auto offset = *reinterpret_cast<int32_t*>(address + 2);
// rip is at address + 6 (size of the instruction)
address = *reinterpret_cast<uintptr_t*>(address + 6 + offset);
}
// if it starts with lea eax,..., it's a delay loaded func
// 48 8D 05 xxxxxxxx
if (address && checkByteSequence(address, {0x48, 0x8d, 0x05})) {
// follow the jmp to the tailMerge func and grab the ImgDelayDescr pointer from there
// do it this way instead of grabbing it from the NT header ourselves because
// we don't know the dll name
auto leaAddress = address + 7 + *reinterpret_cast<int32_t*>(address + 3);
auto jmpOffset = *reinterpret_cast<int32_t*>(address + 7 + 1);
auto tailMergeAddr = address + 7 + jmpOffset + 5;
// this is quite a scary offset, but lets hope it works
auto leaAddr = tailMergeAddr + 51;
// make sure leaAddr is pointing to a lea rcx, [rip + ...]
if (leaAddr && checkByteSequence(leaAddr, {0x48, 0x8d, 0x0d})) {
auto offset = *reinterpret_cast<int32_t*>(leaAddr + 3);
auto did = reinterpret_cast<PCImgDelayDescr>(leaAddr + 7 + offset);
address = reinterpret_cast<intptr_t>(__delayLoadHelper2(did, reinterpret_cast<FARPROC*>(leaAddress)));
if (address == -1) {
address = 0;
}
}
}
// if theres a jmp at the start
if (address && *reinterpret_cast<uint8_t*>(address) == 0xE9) {
auto relative = *reinterpret_cast<uint32_t*>(address + 1);
auto newAddress = address + relative + 5;
address = newAddress;
}
#endif
return address;