#include "MonoLoader.hpp" #include #include HMODULE MonoLoader::WaitForMono(const char* monoName) { HMODULE mono = nullptr; while (!mono) { mono = GetModuleHandleA(monoName); std::this_thread::sleep_for(std::chrono::milliseconds(200)); } return mono; } bool MonoLoader::Initialize(std::shared_ptr log, const char* monoName) { if (log) logger = log; HMODULE mono = WaitForMono(monoName); if (!mono) { logger->error("Mono module not found."); return false; } logger->info("Mono DLL found at {:p}", (void*)mono); mono_get_root_domain = (MonoDomain * (*)())GetProcAddress(mono, "mono_get_root_domain"); mono_thread_attach = (MonoThread * (*)(MonoDomain*))GetProcAddress(mono, "mono_thread_attach"); mono_domain_assembly_open = (MonoAssembly * (*)(MonoDomain*, const char*))GetProcAddress(mono, "mono_domain_assembly_open"); mono_assembly_get_image = (MonoImage * (*)(MonoAssembly*))GetProcAddress(mono, "mono_assembly_get_image"); mono_class_from_name = (MonoClass * (*)(MonoImage*, const char*, const char*))GetProcAddress(mono, "mono_class_from_name"); mono_class_get_method_from_name = (MonoMethod * (*)(MonoClass*, const char*, int))GetProcAddress(mono, "mono_class_get_method_from_name"); mono_compile_method = (void* (*)(MonoMethod*))GetProcAddress(mono, "mono_compile_method"); mono_assembly_foreach = (void(*)(void(*)(MonoAssembly*, void*), void*))GetProcAddress(mono, "mono_assembly_foreach"); mono_assembly_get_name = (mono_assembly_get_name_t)GetProcAddress(mono, "mono_assembly_get_name"); mono_assembly_name_get_name = (mono_assembly_name_get_name_t)GetProcAddress(mono, "mono_assembly_name_get_name"); mono_class_get_methods = (mono_class_get_methods_t)GetProcAddress(mono, "mono_class_get_methods"); mono_method_get_name = (mono_method_get_name_t)GetProcAddress(mono, "mono_method_get_name"); mono_method_signature = (mono_method_signature_t)GetProcAddress(mono, "mono_method_signature"); mono_signature_get_param_count = (mono_signature_get_param_count_t)GetProcAddress(mono, "mono_signature_get_param_count"); mono_class_get_field_from_name = (mono_class_get_field_from_name_t)GetProcAddress(mono, "mono_class_get_field_from_name"); mono_runtime_invoke = (mono_runtime_invoke_t) GetProcAddress(mono, "mono_runtime_invoke"); mono_field_get_offset = (mono_field_get_offset_t)GetProcAddress(mono, "mono_field_get_offset"); mono_field_set_value = (mono_field_set_value_t)GetProcAddress(mono, "mono_field_set_value"); mono_field_get_value = (mono_field_get_value_t)GetProcAddress(mono, "mono_field_get_value"); if (!mono_get_root_domain || !mono_thread_attach || !mono_domain_assembly_open || !mono_assembly_get_image || !mono_class_from_name || !mono_class_get_method_from_name || !mono_compile_method || !mono_assembly_foreach || !mono_assembly_get_name || !mono_assembly_name_get_name || !mono_class_get_methods || !mono_method_get_name || !mono_method_signature || !mono_signature_get_param_count || !mono_class_get_field_from_name || !mono_runtime_invoke || !mono_field_get_offset || !mono_field_set_value || !mono_field_get_value) { logger->error("Failed to resolve one or more Mono exports."); return false; } g_monoDomain = mono_get_root_domain(); if (!g_monoDomain) { logger->error("Mono root domain is null."); return false; } mono_thread_attach(g_monoDomain); logger->info("Attached to Mono domain {:p}", (void*)g_monoDomain); return true; } struct FindAssemblyData { MonoLoader* loader; const std::string* targetName; MonoAssembly* result = nullptr; }; void MonoLoader::FindAssemblyCallback(MonoAssembly* assembly, void* userData) { auto data = reinterpret_cast(userData); if (!assembly || !data || !data->targetName) return; const void* nameStruct = data->loader->mono_assembly_get_name(assembly); const char* nameCStr = data->loader->mono_assembly_name_get_name(nameStruct); if (nameCStr && *data->targetName == nameCStr) { data->result = assembly; } } MonoAssembly* MonoLoader::FindAssembly(const std::string& name) { if (!mono_assembly_foreach || !mono_assembly_get_name || !mono_assembly_name_get_name) { logger->error("Mono exports not resolved, cannot enumerate assemblies."); return nullptr; } FindAssemblyData data; data.loader = this; data.targetName = &name; data.result = nullptr; mono_assembly_foreach(MonoLoader::FindAssemblyCallback, &data); return data.result; } MonoClass* MonoLoader::FindClass(const std::string& assemblyName, const std::string& namespaceName, const std::string& className) { MonoAssembly* assembly = FindAssembly(assemblyName); if (!assembly) return nullptr; MonoImage* image = mono_assembly_get_image(assembly); if (!image) return nullptr; return mono_class_from_name(image, namespaceName.c_str(), className.c_str()); } uint8_t* MonoLoader::GetCompiledMethod(MonoClass* monoClass, const std::string& methodName, int paramCount) { if (!monoClass) return nullptr; MonoMethod* method = mono_class_get_method_from_name(monoClass, methodName.c_str(), paramCount); if (!method) return nullptr; return (uint8_t*)mono_compile_method(method); } MonoThread* MonoLoader::AttachCurrentThread() { MonoThread* monoThread = mono_thread_attach(mono_get_root_domain()); if (!monoThread) return nullptr; return monoThread; } void MonoLoader::DetachCurrentThread(MonoThread* thread) { if (!thread || !mono_thread_detach) return; mono_thread_detach(thread); } MonoMethod* MonoLoader::GetMethod(MonoClass* monoClass, const std::string& methodName, int paramCount) { if (!monoClass) return nullptr; MonoMethod* method = mono_class_get_method_from_name(monoClass, methodName.c_str(), paramCount); if (!method) return nullptr; return method; } MonoObject* MonoLoader::InvokeMethod(MonoMethod* method, MonoObject* obj, void** params, MonoObject** exception) { if (!method) return nullptr; MonoThread* thread = AttachCurrentThread(); if (!thread) return nullptr; MonoObject* invoke = mono_runtime_invoke(method, obj, params, exception); DetachCurrentThread(thread); return invoke; } void MonoLoader::DumpMethods(MonoClass* monoClass, std::shared_ptr logger) { if (!monoClass) { logger->error("DumpMethods: monoClass is null."); return; } if (!g_monoDomain) { logger->error("DumpMethods: Mono domain is null."); return; } mono_thread_attach(g_monoDomain); if (!mono_class_get_methods || !mono_method_get_name || !mono_method_signature || !mono_signature_get_param_count) { logger->error("DumpMethods: Required Mono exports not resolved."); return; } logger->info("---- Dumping methods ----"); void* iter = nullptr; MonoMethod* method = nullptr; while ((method = mono_class_get_methods(monoClass, &iter)) != nullptr) { const char* name = mono_method_get_name(method); MonoMethodSignature* sig = mono_method_signature(method); uint32_t paramCount = 0; if (sig) paramCount = mono_signature_get_param_count(sig); logger->info("Method: {} | Params: {}", name ? name : "null", paramCount); } logger->info("-------------------------"); }