From eab56ed704fb4915878ea72180bb05e3560eeb3c Mon Sep 17 00:00:00 2001 From: illusion0001 <37698908+illusion0001@users.noreply.github.com> Date: Sat, 16 Nov 2024 03:49:22 -0600 Subject: [PATCH] loader: remove need to run in loop for domain pointer this changelist patches caller to obtain final domain pointer to init our loader. jit is patched on boot because it won't work after domain pointer function is called --- CSharpLoaderDll/dllmain.cpp | 232 ++++++++++++++++++++++++++---------- 1 file changed, 168 insertions(+), 64 deletions(-) diff --git a/CSharpLoaderDll/dllmain.cpp b/CSharpLoaderDll/dllmain.cpp index 43e3461..f6a1deb 100644 --- a/CSharpLoaderDll/dllmain.cpp +++ b/CSharpLoaderDll/dllmain.cpp @@ -13,6 +13,7 @@ enum class DllType { }; static DllType dllType = DllType::Unknown; +static HMODULE g_hModule = 0; struct MonoAssemblyOpenRequest { @@ -58,59 +59,31 @@ struct MonoError { }; typedef int (*ves_icall_System_AppDomain_ExecuteAssembly_t)(_MonoAppDomain** ad, _MonoReflectionAssembly** refass, void** args, MonoError* error); -DWORD WINAPI MainThread(LPVOID dwModule) +typedef void* (*writeDomainPtr_t)(); + +auto writeDomainPtr = (writeDomainPtr_t)0; + +DWORD WINAPI MainThread(LPVOID dwModule); + +void* domain = nullptr; + +static void* WriteDomainPtrHook() { - if (dllType == DllType::Version) + if (writeDomainPtr) { - Sleep(3000); - } - const char* configFile = "./CSharpLoader/b1cs.ini"; - UINT enableConsole = GetPrivateProfileIntA("Settings", "Console", 0, configFile); - BOOL enableJit = GetPrivateProfileIntA("Settings", "EnableJit", 1, configFile); - if (enableConsole == 1) { - AllocConsole(); - FILE* fDummy; - freopen_s(&fDummy, "CONIN$", "r", stdin); - freopen_s(&fDummy, "CONOUT$", "w", stdout); - freopen_s(&fDummy, "CONOUT$", "w", stderr); + domain = writeDomainPtr(); + wprintf_s(L"domain: 0x%p\n", domain); + CreateThread(nullptr, 0, MainThread, g_hModule, 0, nullptr); + //MainThread(0); } + return domain; +} + +DWORD WINAPI MainThread(LPVOID dwModule) +{ loadPluginDlls(); - std::cout << "CSharpLoader enableJit: " << enableJit << std::endl; std::cout << "CSharpLoader wait for init." << std::endl; - // enable jit - if (enableJit) { - uint64_t memory_fuction_ptr = signature("83 3D ? ? ? ? 00 0F 84 ? ? ? ? C7 84 24 ? ? 00 00 01 00 00 00").GetPointer(); - if (memory_fuction_ptr == 0) { - std::cout << "memory function signature found." << std::endl; - } else { - DWORD old_protect; - if (VirtualProtect((void*)(memory_fuction_ptr + 7), 2, PAGE_EXECUTE_READWRITE, &old_protect)) { - *(uint16_t*)(memory_fuction_ptr + 7) = 0xE990; // nop; jmp - VirtualProtect((void*)(memory_fuction_ptr + 7), 2, old_protect, &old_protect); - uint64_t mono_mode_ptr = signature("48 8D 0D ? ? ? ? E8 ? ? ? ? 89 44 24 ? 83 7C 24 ? 00").GetPointer(); - if (mono_mode_ptr == 0) { - std::cout << "mono_mode signature found." << std::endl; - } else { - if (VirtualProtect((void*)(mono_mode_ptr + 7), 5, PAGE_EXECUTE_READWRITE, &old_protect)) { - *(uint8_t*)(mono_mode_ptr + 7) = 0xB8; - *(uint32_t*)(mono_mode_ptr + 8) = 1; - VirtualProtect((void*)(mono_mode_ptr + 7), 5, old_protect, &old_protect); - } - } - } - } - } - signature domain_s("F0 FF 88 B0 00 00 00 48 8B 05 ? ? ? ? 48 3B D8 49 0F 44 C4"); - if (domain_s.GetPointer() == 0) { - std::cout << "domainPtr pattern not found." << std::endl; - return EXIT_FAILURE; - } - void **domainPtr = (void**)domain_s.instruction(10).add(14).GetPointer(); - if (domainPtr == nullptr) { - std::cout << "domainPtr not found." << std::endl; - return EXIT_FAILURE; - } auto mono_thread_internal_attach = (mono_thread_internal_attach_t)signature( "40 57 48 83 EC 30 8B 15 ? ? ? ? 48 8B F9 65 48 8B 04 25 58 00 00 00 B9 A8 02 00 00 48 8B 04 D0 48 83 3C 01 00").GetPointer(); if (mono_thread_internal_attach == nullptr) { @@ -129,19 +102,11 @@ DWORD WINAPI MainThread(LPVOID dwModule) std::cout << "ves_icall_System_AppDomain_ExecuteAssembly not found." << std::endl; return EXIT_FAILURE; } - void* domain = nullptr; - for (size_t i = 0; i < 180; i++) { - domain = *domainPtr; - if (domain != nullptr) { - break; - } - Sleep(1000); // 1s - } + if (domain == nullptr) { std::cout << "domain is null." << std::endl; return EXIT_FAILURE; } - Sleep(2000); // 2s void* mono_thread = mono_thread_internal_attach(domain); if (mono_thread == nullptr) { @@ -152,9 +117,9 @@ DWORD WINAPI MainThread(LPVOID dwModule) MonoAssemblyOpenRequest open_request{}; MonoImageOpenStatus status = MonoImageOpenStatus::MONO_IMAGE_OK; - wchar_t fullFilename[MAX_PATH]; + wchar_t fullFilename[MAX_PATH]{}; GetFullPathName(L"CSharpLoader\\CSharpManager.bin", MAX_PATH, fullFilename, nullptr); - char fullFilenameA[MAX_PATH]; + char fullFilenameA[MAX_PATH]{}; // convert to utf-8 to support Chinese path WideCharToMultiByte(CP_UTF8, 0, fullFilename, MAX_PATH, fullFilenameA, MAX_PATH, NULL, NULL); void* assembly = mono_assembly_request_open(fullFilenameA, &open_request, &status); @@ -180,15 +145,153 @@ DWORD WINAPI MainThread(LPVOID dwModule) return EXIT_SUCCESS; } +namespace Memory +{ + bool CallFunction32(void* src, void* dst, int len) + { + if (!src || !dst || len < 5) + { + return false; + } + DWORD curProtection; + VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &curProtection); + + memset(src, 0x90, len); + + uintptr_t relativeAddress = ((uintptr_t)dst - (uintptr_t)src) - 5; + + *(BYTE*)src = 0xE8; + *(uint32_t*)((uintptr_t)src + 1) = relativeAddress; + + DWORD temp; + VirtualProtect(src, len, curProtection, &temp); + + return true; + } + + void* DetourFunction64(void* pSource, void* pDestination, DWORD dwLen) + { + constexpr DWORD MinLen = 14; + + if (dwLen < MinLen) + { + return nullptr; + } + + BYTE stub[] = { + 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [$+6] + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ptr + }; + + DWORD dwOld = 0; + VirtualProtect(pSource, dwLen, PAGE_EXECUTE_READWRITE, &dwOld); + + // orig + memcpy(stub + 6, &pDestination, 8); + memcpy(pSource, stub, sizeof(stub)); + + for (DWORD i = MinLen; i < dwLen; i++) + { + *(BYTE*)((DWORD_PTR)pSource + i) = 0x90; + } + + VirtualProtect(pSource, dwLen, dwOld, &dwOld); + return pDestination; + } +}; + +static void Make32to64Call(uintptr_t source_target, uintptr_t second_jmp, uintptr_t target_jmp, uint32_t source_size, const wchar_t* source_name = L"", const wchar_t* second_jmp_name = L"", const wchar_t* target_jmp_name = L"") +{ + if (!source_target || !second_jmp || !target_jmp || source_size < 5) + { + wprintf_s(L"Canoot create jump '%s' from '%s' to '%s'\n", source_name, second_jmp_name, target_jmp_name); + wprintf_s(L"source_target: 0x%llx\n", source_target); + wprintf_s(L"source_size: %u bytes\n", source_size); + wprintf_s(L"second_jmp: 0x%llx\n", second_jmp); + wprintf_s(L"target_jmp: 0x%llx\n", target_jmp); + return; + } + Memory::CallFunction32((void*)source_target, (void*)second_jmp, source_size); + wprintf_s(L"Created jump %s (0x%016llx) to %s (0x%016llx)\n", source_name, (uintptr_t)source_target, second_jmp_name, (uintptr_t)second_jmp); + Memory::DetourFunction64((void*)second_jmp, (void*)target_jmp, 14); + wprintf_s(L"Created jump %s (0x%016llx) to %s (0x%016llx)\n", second_jmp_name, (uintptr_t)second_jmp, target_jmp_name, (uintptr_t)target_jmp); +} + +static void enableJitPatch() +{ + // if (enableJit) + { + uint64_t memory_fuction_ptr = signature("83 3D ? ? ? ? 00 0F 84 ? ? ? ? C7 84 24 ? ? 00 00 01 00 00 00").GetPointer(); + if (memory_fuction_ptr == 0) { + std::cout << "memory function signature not found." << std::endl; + } + else { + DWORD old_protect; + if (VirtualProtect((void*)(memory_fuction_ptr + 7), 2, PAGE_EXECUTE_READWRITE, &old_protect)) { + *(uint16_t*)(memory_fuction_ptr + 7) = 0xE990; // nop; jmp + VirtualProtect((void*)(memory_fuction_ptr + 7), 2, old_protect, &old_protect); + uint64_t mono_mode_ptr = signature("48 8D 0D ? ? ? ? E8 ? ? ? ? 89 44 24 ? 83 7C 24 ? 00").GetPointer(); + if (mono_mode_ptr == 0) { + std::cout << "mono_mode signature not found." << std::endl; + } + else { + if (VirtualProtect((void*)(mono_mode_ptr + 7), 5, PAGE_EXECUTE_READWRITE, &old_protect)) { + *(uint8_t*)(mono_mode_ptr + 7) = 0xB8; + *(uint32_t*)(mono_mode_ptr + 8) = 1; + VirtualProtect((void*)(mono_mode_ptr + 7), 5, old_protect, &old_protect); + } + } + } + } + } +} + +static void StartupPatch() +{ + const char* configFile = "./CSharpLoader/b1cs.ini"; + BOOL enableConsole{}, enableJit{}; + enableConsole = GetPrivateProfileIntA("Settings", "Console", 0, configFile); + enableJit = GetPrivateProfileIntA("Settings", "EnableJit", 1, configFile); + if (enableConsole) + { + AllocConsole(); + FILE* fDummy; + freopen_s(&fDummy, "CONIN$", "r", stdin); + freopen_s(&fDummy, "CONOUT$", "w", stdout); + freopen_s(&fDummy, "CONOUT$", "w", stderr); + std::cout << "CSharpLoader enableConsole: " << enableConsole << std::endl; + } + // required for harmony to work? + // without it patches applied don't seem to work + if (enableJit) + { + std::cout << "CSharpLoader enableJit: " << enableJit << std::endl; + enableJitPatch(); + } + auto WriteMonoDomainPtrFuncAddr = signature("E8 ? ? ? ? 8B 15 ? ? ? ? 65 48 8B 0C 25 58 00 00 00 41 B8 98 02 00 00 48 89 05 ? ? ? ? 48 89 05 ? ? ? ?"); + const uintptr_t WriteMonoDomainPtrFuncAddr2 = WriteMonoDomainPtrFuncAddr.GetPointer(); + const uintptr_t Int3Jmp = signature("CC CC CC CC CC CC CC CC CC CC CC CC CC CC").GetPointer(); + const uintptr_t WriteDomainHook = (uintptr_t)&WriteDomainPtrHook; + wprintf_s(L"WriteMonoDomainPtrFuncAddr: 0x%llx\n", WriteMonoDomainPtrFuncAddr.GetPointer()); + if (WriteMonoDomainPtrFuncAddr2 && Int3Jmp && WriteDomainHook) + { + writeDomainPtr = (writeDomainPtr_t)WriteMonoDomainPtrFuncAddr.instruction(1).add(5).GetPointer(); + if (writeDomainPtr) + { + Make32to64Call(WriteMonoDomainPtrFuncAddr2, Int3Jmp, WriteDomainHook, 5); + } + } +} + void init_dll(HMODULE hModule) { DisableThreadLibraryCalls(hModule); - wchar_t moduleFullpathFilename[MAX_PATH + 1]; + wchar_t moduleFullpathFilename[MAX_PATH + 1]{}; GetModuleFileNameW(hModule, moduleFullpathFilename, static_cast(std::size(moduleFullpathFilename))); - wchar_t fname[_MAX_FNAME+1]; + wchar_t fname[_MAX_FNAME + 1]{}; { - wchar_t drive[_MAX_DRIVE+1]; - wchar_t dir[_MAX_DIR+1]; - wchar_t ext[_MAX_EXT+1]; + wchar_t drive[_MAX_DRIVE + 1]{}; + wchar_t dir[_MAX_DIR + 1]{}; + wchar_t ext[_MAX_EXT + 1]{}; _wsplitpath_s(moduleFullpathFilename, drive, dir, fname, ext); } if (_wcsicmp(fname, L"version") == 0) { @@ -198,7 +301,7 @@ void init_dll(HMODULE hModule) { dllType = DllType::Hid; init_hid_dll(); } - CreateThread(nullptr, 0, MainThread, hModule, 0, nullptr); + StartupPatch(); } void deinit_dll() { @@ -220,6 +323,7 @@ BOOL APIENTRY DllMain( HMODULE hModule, switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: + g_hModule = hModule; std::call_once(initFlag, [&]() { init_dll(hModule); }); break; case DLL_PROCESS_DETACH: