#include #include #include #include #include #include #include #include #include static std::wstring StringToWideString(const std::string& text, UINT encoding=CP_UTF8) { std::vector buffer(text.size() + 1); int length = MultiByteToWideChar(encoding, 0, text.c_str(), text.size() + 1, buffer.data(), buffer.size()); return std::wstring(buffer.data(), length - 1); } std::string WideStringToString(const std::wstring& text,UINT cp=CP_UTF8) { std::vector buffer((text.size() + 1) * 4); WideCharToMultiByte(cp, 0, text.c_str(), -1, buffer.data(), buffer.size(), nullptr, nullptr); return buffer.data(); } HANDLE runexe(const std::wstring &exe,const std::optional &startup_argument) { STARTUPINFOW si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); std::vectorargu; if(startup_argument.has_value()){ argu.resize(startup_argument.value().size()+1); wcscpy(argu.data(),startup_argument.value().c_str()); } CreateProcessW( exe.c_str(), // No module name (use command line) startup_argument.has_value()?argu.data(): NULL, NULL, // Process handle not inheritable NULL, // Thread handle not inheritable FALSE, // Set handle inheritance to FALSE 0, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure &pi ); // Pointer to PROCESS_INFORMATION structure return pi.hProcess; } std::wstring stolower(const std::wstring& s1){ auto s=s1; std::transform(s.begin(), s.end(), s.begin(), tolower); return s; } std::vector EnumerateProcesses(const std::wstring& exe) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { return {}; } PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hSnapshot, &pe32)) { CloseHandle(hSnapshot); return {}; } std::vector pids; do { if(stolower(exe)==stolower(pe32.szExeFile)) pids.push_back(pe32.th32ProcessID); } while (Process32Next(hSnapshot, &pe32)); CloseHandle(hSnapshot); return pids; } enum { STRING = 12, MESSAGE_SIZE = 500, PIPE_BUFFER_SIZE = 50000, SHIFT_JIS = 932, MAX_MODULE_SIZE = 120, PATTERN_SIZE = 30, HOOK_NAME_SIZE = 60, FIXED_SPLIT_VALUE = 0x10001 , HOOKCODE_LEN=500}; struct ThreadParam { bool operator==(ThreadParam other) const { return processId == other.processId && addr == other.addr && ctx == other.ctx && ctx2 == other.ctx2; } DWORD processId; uint64_t addr; uint64_t ctx; // The context of the hook: by default the first value on stack, usually the return address uint64_t ctx2; // The subcontext of the hook: 0 by default, generated in a method specific to the hook }; struct messagelist{ bool read; int type; DWORD pid; char name[HOOK_NAME_SIZE]; wchar_t hookcode[HOOKCODE_LEN]; ThreadParam tp; wchar_t* stringptr; uint64_t addr; }; class lunapatch{ public: HANDLE hMessage; HANDLE hwait; std::wstring target_exe; nlohmann::json config; std::maptranslation; std::unordered_setconnectedpids; void (*Luna_Start)( HANDLE* hRead ); void (*Luna_Inject)(DWORD pid,LPCWSTR basepath); void (*Luna_EmbedSettings)(DWORD pid,UINT32 waittime,UINT8 fontCharSet,bool fontCharSetEnabled,wchar_t *fontFamily,UINT32 spaceadjustpolicy,UINT32 keeprawtext,bool fastskipignore); void (*Luna_useembed)(DWORD pid,uint64_t address,uint64_t ctx1,uint64_t ctx2,bool use); bool (*Luna_checkisusingembed)(DWORD pid,uint64_t address,uint64_t ctx1,uint64_t ctx2); void (*Luna_embedcallback)(DWORD pid,LPCWSTR text,LPCWSTR trans); std::setnotranslation; lunapatch(std::wstring dll,nlohmann::json&&_translation,nlohmann::json&&_config):translation(_translation),config(_config){ auto LunaHost=LoadLibraryW(dll.c_str()); Luna_Start=(decltype(Luna_Start))GetProcAddress(LunaHost,"Luna_Start"); Luna_EmbedSettings=(decltype(Luna_EmbedSettings))GetProcAddress(LunaHost,"Luna_EmbedSettings"); Luna_Inject=(decltype(Luna_Inject))GetProcAddress(LunaHost,"Luna_Inject"); Luna_useembed=(decltype(Luna_useembed))GetProcAddress(LunaHost,"Luna_useembed"); Luna_checkisusingembed=(decltype(Luna_checkisusingembed))GetProcAddress(LunaHost,"Luna_checkisusingembed"); Luna_embedcallback=(decltype(Luna_embedcallback))GetProcAddress(LunaHost,"Luna_embedcallback"); Luna_Start(&hMessage); std::thread([&](){Parsehostmessage();}).detach(); } void run(){ target_exe=StringToWideString(config["target_exe"]); auto _startup_argument=config["startup_argument"]; std::optional startup_argument; if(_startup_argument.is_null()) startup_argument={}; else startup_argument=StringToWideString(config["startup_argument"]); hwait=runexe(target_exe,startup_argument); } ~lunapatch(){ if(notranslation.size()){ for(auto &text:notranslation){ translation[text]=""; } auto notrs=nlohmann::json(notranslation).dump(4); std::ofstream of; of.open("no_translation.json"); of< translation=nlohmann::json::parse(jsonfile); jsonfile.close(); bool isbit64=configjson["isbit64"]; auto bitappendix=isbit64?L"64":L"32"; auto LunaHost=(curr/(std::wstring(L"LunaHost")+bitappendix)).wstring(); auto LunaHook=(curr/(std::wstring(L"LunaHook")+bitappendix)).wstring(); lunapatch _lunapatch(LunaHost,std::move(translation),std::move(configjson)); _lunapatch.run(); _lunapatch.inject(); _lunapatch.wait(); return true; }