Посмотрите видео ниже, чтобы узнать, как установить наш сайт в виде веб-приложения на главном экране.
Примечание: Эта функция может быть недоступна в некоторых браузерах.
Для просмотра скрытого содержимого вы должны войти или зарегистрироваться.
C++:#include "pch.h" #include <windows.h> #include <string> #include <shellapi.h> #include <iostream> #include <cwctype> // Глобальные переменные std::wstring WindowTitle = L"Lineage II"; // по умолчанию HWND TargetWindow = nullptr; // Типы функций using SetTextFn = void(__thiscall*)(void*, const wchar_t*); // Оригинальная функция и хук SetTextFn OriginalSetText = nullptr; // Хук для SetText void __fastcall HookedSetText(void* thisptr, void*, const wchar_t* /*text*/) { TargetWindow = *(HWND*)((DWORD)thisptr + 4); SetWindowTextW(TargetWindow, WindowTitle.c_str()); } // Установка хука bool InstallHook() { HMODULE windowDll = GetModuleHandleW(L"Window.dll"); if (!windowDll) return false; OriginalSetText = (SetTextFn)GetProcAddress(windowDll, "?SetText@WWindow@@UAEXPBG@Z"); if (!OriginalSetText) return false; DWORD oldProtect; VirtualProtect(OriginalSetText, 5, PAGE_EXECUTE_READWRITE, &oldProtect); *(BYTE*)OriginalSetText = 0xE9; *(DWORD*)((DWORD)OriginalSetText + 1) = (DWORD)HookedSetText - ((DWORD)OriginalSetText + 5); VirtualProtect(OriginalSetText, 5, oldProtect, &oldProtect); return true; } // Экспорт тестовых функций extern "C" __declspec(dllexport) void __stdcall function1() {} extern "C" __declspec(dllexport) void __stdcall L2ExportedFunction() {} namespace { enum L2ConsoleState { Loading = 0, Unknown = 1, Login = 2, CharCreate = 3, CharSelect = 4, InGame = 5 }; class UL2ConsoleWnd {}; UL2ConsoleWnd* UL2ConsoleWndPtr = nullptr; uintptr_t consoleOffset = 0x3663bc; // для IL клиента } class UNetworkHandler {}; typedef int(__fastcall* RequestAuthLoginFn)(UNetworkHandler*, int, const wchar_t*, const wchar_t*, int); const uintptr_t unetworkOffset = 0x81F538; UNetworkHandler** unetwork = nullptr; RequestAuthLoginFn requestAuthLoginFn = nullptr; // Конфиг для автологина struct AutoLoginConfig { std::wstring login; std::wstring password; std::wstring title; // новое поле int countenter = 0; int delay = 2000; }; // --- хелперы --- static std::wstring ToLower(std::wstring s) { for (auto& ch : s) ch = static_cast<wchar_t>(towlower(ch)); return s; } static bool IsTrueValue(const std::wstring& v) { auto vl = ToLower(v); return (vl == L"1" || vl == L"true" || vl == L"yes" || vl == L"on"); } // --- логин --- static void RequestLogin(const std::wstring& login, const std::wstring& password) { if (!unetwork || !requestAuthLoginFn) return; if (login.empty() || password.empty()) return; requestAuthLoginFn(*unetwork, 0, login.c_str(), password.c_str(), 0); } // --- эмуляция Enter --- static void SimulateEnter() { INPUT input[2] = {}; input[0].type = INPUT_KEYBOARD; input[0].ki.wVk = VK_RETURN; input[1].type = INPUT_KEYBOARD; input[1].ki.wVk = VK_RETURN; input[1].ki.dwFlags = KEYEVENTF_KEYUP; SendInput(2, input, sizeof(INPUT)); } // --- чтение настроек из ini файла --- static bool ReadIniConfig(AutoLoginConfig& cfg) { wchar_t buffer[256] = { 0 }; GetPrivateProfileStringW(L"AutoLogin", L"Login", L"", buffer, _countof(buffer), L".\\AutoLogin.ini"); cfg.login = buffer; ZeroMemory(buffer, sizeof(buffer)); GetPrivateProfileStringW(L"AutoLogin", L"Password", L"", buffer, _countof(buffer), L".\\AutoLogin.ini"); cfg.password = buffer; ZeroMemory(buffer, sizeof(buffer)); GetPrivateProfileStringW(L"AutoLogin", L"Title", L"", buffer, _countof(buffer), L".\\AutoLogin.ini"); cfg.title = buffer; // читаем title cfg.countenter = GetPrivateProfileIntW(L"AutoLogin", L"countenter", 0, L".\\AutoLogin.ini"); cfg.delay = GetPrivateProfileIntW(L"AutoLogin", L"delay", 2000, L".\\AutoLogin.ini"); // Возвращаем "готовность" только для логина, как и раньше return !cfg.login.empty() && !cfg.password.empty(); } // --- парсинг аргументов командной строки --- static bool ParseCommandLine(AutoLoginConfig& cfg, bool& anyArgsOut) { int argc = 0; LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (!argv) { anyArgsOut = false; return false; } bool anyArgs = (argc > 1); for (int i = 1; i < argc; i++) { std::wstring arg = argv[i]; if (arg.empty()) continue; if (arg[0] == L'/' || arg[0] == L'-') { arg.erase(0, 1); } size_t pos = arg.find(L'='); if (pos == std::wstring::npos) pos = arg.find(L':'); std::wstring key = (pos == std::wstring::npos) ? arg : arg.substr(0, pos); std::wstring value = (pos == std::wstring::npos) ? L"1" : arg.substr(pos + 1); key = ToLower(key); if (key == L"account" || key == L"login" || key == L"user" || key == L"username") { cfg.login = value; } else if (key == L"password" || key == L"pass" || key == L"pwd") { cfg.password = value; } else if (key == L"countenter" || key == L"entercount") { cfg.countenter = _wtoi(value.c_str()); } else if (key == L"delay" || key == L"enterdelay") { cfg.delay = _wtoi(value.c_str()); } else if (key == L"title" || key == L"windowtitle") { cfg.title = value; // <--- забираем title из аргумента } } LocalFree(argv); anyArgsOut = anyArgs; // как и раньше, "успех" основан на наличии пары логин/пароль return !cfg.login.empty() && !cfg.password.empty(); } static bool WaitForValidState(L2ConsoleState* statePtr) { DWORD startTime = GetTickCount(); while (*statePtr == L2ConsoleState::Loading) { if (GetTickCount() - startTime > 30000) return false; Sleep(300); } if (*statePtr == L2ConsoleState::Login) return true; if (*statePtr == L2ConsoleState::Unknown) { startTime = GetTickCount(); while (*statePtr == L2ConsoleState::Unknown) { if (GetTickCount() - startTime > 30000) return false; Sleep(300); } return (*statePtr == L2ConsoleState::Login); } return false; } // --- Функции для центрирования окна --- struct FindWindowData { DWORD pid; HWND hWnd; }; static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) { FindWindowData* data = reinterpret_cast<FindWindowData*>(lParam); DWORD wndPid; GetWindowThreadProcessId(hWnd, &wndPid); if (wndPid == data->pid) { wchar_t className[256]; GetClassNameW(hWnd, className, ARRAYSIZE(className)); if (wcscmp(className, L"L2UnrealWWindowsViewportWindow") == 0) { data->hWnd = hWnd; return FALSE; // нашли — прекращаем поиск } } return TRUE; } static HWND FindMyL2Window() { FindWindowData data{}; data.pid = GetCurrentProcessId(); data.hWnd = nullptr; EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&data)); return data.hWnd; } static bool IsWindowFullscreen(HWND hWnd) { RECT rcWnd, rcMon; if (!GetWindowRect(hWnd, &rcWnd)) return false; HMONITOR hMon = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); MONITORINFO mi{ sizeof(mi) }; if (!GetMonitorInfoW(hMon, &mi)) return false; rcMon = mi.rcMonitor; LONG style = GetWindowLongW(hWnd, GWL_STYLE); // fullscreen = окно занимает весь монитор И у него нет обычных рамок bool sizeMatch = (rcWnd.left == rcMon.left && rcWnd.top == rcMon.top && rcWnd.right == rcMon.right && rcWnd.bottom == rcMon.bottom); bool hasBorders = (style & WS_OVERLAPPEDWINDOW) != 0; return (sizeMatch && !hasBorders); } static void CenterL2Window() { HWND hWnd = nullptr; // ждём не более 5 секунд, потом выходим DWORD start = GetTickCount(); while (!hWnd && (GetTickCount() - start < 5000)) { hWnd = FindMyL2Window(); Sleep(500); } if (!hWnd) return; // окно не нашли — выходим if (IsWindowFullscreen(hWnd)) { return; // fullscreen — не трогаем } HMONITOR hMon = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); MONITORINFO mi{ sizeof(mi) }; GetMonitorInfoW(hMon, &mi); RECT rcWnd; GetWindowRect(hWnd, &rcWnd); int wndWidth = rcWnd.right - rcWnd.left; int wndHeight = rcWnd.bottom - rcWnd.top; int screenWidth = mi.rcWork.right - mi.rcWork.left; int screenHeight = mi.rcWork.bottom - mi.rcWork.top; int x = mi.rcWork.left + (screenWidth - wndWidth) / 2; int y = mi.rcWork.top + (screenHeight - wndHeight) / 2; SetWindowPos(hWnd, nullptr, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW); } static void L2StatusLoad() { HMODULE hNwindowModule = nullptr; while (!hNwindowModule) { hNwindowModule = GetModuleHandleW(L"nwindow.dll"); Sleep(1000); } uintptr_t pUL2ConsoleWnd = (reinterpret_cast<uintptr_t>(hNwindowModule)) + consoleOffset; while (!UL2ConsoleWndPtr) { UL2ConsoleWndPtr = *reinterpret_cast<UL2ConsoleWnd**>(pUL2ConsoleWnd); Sleep(300); } // корректное вычисление адреса поля по смещению L2ConsoleState* statePtr = reinterpret_cast<L2ConsoleState*>( reinterpret_cast<uintptr_t>(UL2ConsoleWndPtr) + 0x38 ); bool authAttempted = false; while (true) { L2ConsoleState currentState = *statePtr; if (currentState == L2ConsoleState::Loading || currentState == L2ConsoleState::Unknown) { if (WaitForValidState(statePtr)) { currentState = *statePtr; } else { Sleep(5000); continue; } } if (currentState == L2ConsoleState::Login && !authAttempted) { AutoLoginConfig cfg; bool anyArgs = false; bool haveCmdCreds = ParseCommandLine(cfg, anyArgs); // --- ПРИМЕНЯЕМ TITLE СРАЗУ, НЕ ЗАВИСИМО ОТ КРЕДОВ --- if (!cfg.title.empty()) { WindowTitle = cfg.title; // из аргумента } else { // если в аргументах title нет — пробуем INI wchar_t tbuf[256] = {}; GetPrivateProfileStringW(L"AutoLogin", L"Title", L"", tbuf, _countof(tbuf), L".\\AutoLogin.ini"); if (tbuf[0] != L'\0') { WindowTitle = tbuf; } } // ----------------------------------------------------- if (anyArgs) { if (!haveCmdCreds) { // есть арг-ты, но нет пары логин/пароль — логин не делаем break; } } else { if (!ReadIniConfig(cfg)) { // логин из ini недоступен — завершаемся break; } } RequestLogin(cfg.login, cfg.password); authAttempted = true; if (cfg.countenter > 0) { for (int i = 0; i < cfg.countenter; i++) { Sleep(cfg.delay); SimulateEnter(); } } Sleep(10000); } else if (currentState == L2ConsoleState::InGame) { break; } Sleep(3000); } } // --- Поток для центрирования окна --- static DWORD WINAPI CenterThread(LPVOID) { Sleep(1000); // ждём появления окна CenterL2Window(); return 0; } // --- Поток для авторизации --- static DWORD WINAPI AuthThread(LPVOID) { Sleep(1000); L2StatusLoad(); return 0; } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID) { if (ul_reason_for_call == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hModule); // --- УСТАНАВЛИВАЕМ TITLE МАКСИМАЛЬНО РАНО (до хука и до первых SetText) --- { AutoLoginConfig tmp; bool any = false; // Используем ParseCommandLine только чтобы вытащить title (возврат игнорируем) (void)ParseCommandLine(tmp, any); if (!tmp.title.empty()) { WindowTitle = tmp.title; // приоритет — аргумент } else { wchar_t tbuf[256] = {}; GetPrivateProfileStringW(L"AutoLogin", L"Title", L"", tbuf, _countof(tbuf), L".\\AutoLogin.ini"); if (tbuf[0] != L'\0') { WindowTitle = tbuf; // если в аргументах нет — берем из ini } // если и там пусто — останется Zaken } } // ---------------------------------------------------------------------------- HMODULE engineModule = GetModuleHandleW(L"engine.dll"); if (engineModule) { unetwork = reinterpret_cast<UNetworkHandler**>(reinterpret_cast<uintptr_t>(engineModule) + unetworkOffset); requestAuthLoginFn = (RequestAuthLoginFn)GetProcAddress(engineModule, "?RequestAuthLogin@UNetworkHandler@@UAEHPAG0H@Z"); } InstallHook(); // Запускаем оба потока CreateThread(nullptr, 0, CenterThread, nullptr, 0, nullptr); CreateThread(nullptr, 0, AuthThread, nullptr, 0, nullptr); } return TRUE; }
Для просмотра скрытого содержимого вы должны войти или зарегистрироваться.