Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 168 additions & 64 deletions CSharpLoaderDll/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ enum class DllType {
};

static DllType dllType = DllType::Unknown;
static HMODULE g_hModule = 0;

struct MonoAssemblyOpenRequest
{
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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<UINT>(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) {
Expand All @@ -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() {
Expand All @@ -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:
Expand Down