Как сделать Автологин Интерлюдия?

Charmant


Только что заметил отличие у тебя 8 а у меня b 0x81F538 - 0xb1f538
И самое странное, что твой hex работает а мой нет хотя в клиенте у мен b вместо 8)

Посмотреть вложение 79175
Может dll накрыта темидой и по этому такая разница.
Попробуй через ghidra сделать декомпил и посмотреть там. 🤔
 

Мне кажется я неправильно высчитываю (опыта мало в этом деле)
Вот я нашел офсет EAX,[DAT_10b1f538] он будет равен 0xb1f538

Самое интересное что твой 0x81F538 работает а мой нет)
У длл есть базовый адрес. 10b1f538 это база + смещение. В ИЛ енжине базовый адрес 10300000, т.е: 0x10b1f538 - 0x10300000 = 0x81F538

У длл есть базовый адрес. 10b1f538 это база + смещение. В ИЛ енжине базовый адрес 10300000, т.е: 0x10b1f538 - 0x10300000 = 0x81F538
Почитайте про виртуальные адреса (VA) и относительные виртуальные адреса (RVA). Станет понятнее
 
Последнее редактирование модератором:
Надо будет в ресурсы добавить хотя бы для it с инструкцией поиска offset наглядно в картинке и тексте с исходниками.
Заодно краткую инструкцию по вычислению offset от базового адреса.
Хорошее дело делаем 🍻
 
У меня возник вопрос, связанный с этой же темой.
Есть ли какой-то флаг в L2, который позволяет определить, что окно с интерфейсом полностью загружено и готово к использованию?
 
У меня возник вопрос, связанный с этой же темой.
Есть ли какой-то флаг в L2, который позволяет определить, что окно с интерфейсом полностью загружено и готово к использованию?
Про сам интерфейс не знаю, но дождаться создания основного нативного окна (пропустив при этом сплэшскрин окно) я выкладывал на шарпе. Думаю стоит попробовать такой вариант, то есть через do-while ждать указатель на окно

C#:
                var splashWindowHandler = IntPtr.Zero;

                // Waiting for splash window handler
                do
                {
                    process.Refresh();
                    splashWindowHandler = process.MainWindowHandle;
                }
                while (splashWindowHandler == IntPtr.Zero);

                var mainWindowHandler = IntPtr.Zero;

                // Waiting for main window handler
                do
                {
                    process.Refresh();
                    if (splashWindowHandler != process.MainWindowHandle)
                    {
                        mainWindowHandler = process.MainWindowHandle;
                    }
                }
                while (mainWindowHandler == IntPtr.Zero);

да и все что делается через событие DLL_PROCESS_ATTACH, стоит (по мимо проверок на nullptr) проверять и что вернула функция GetProcAddress. Возможно функция вернула также nullptr, по сколько dll engine по какой-то причине еще не прогрузилась, и адрес на процедуру RequestAuthLogin@UNetworkHandler@@UAEHPAG0H@Z был не доступен.

C++:
void Init()
{
    HMODULE hEngineModule = GetModuleHandleW(L"engine.dll");
    if (hEngineModule != nullptr)
    {
        uintptr_t UNetworkAddress = (reinterpret_cast<uintptr_t>(hEngineModule)) + UNetworkOffset;
        UNetwork = *reinterpret_cast<UNetworkHandler**>(UNetworkAddress);

        MessageBox(NULL, L"Все данные собраны вход!!!!!", L"", MB_OK);

        fRequestAuthLogin = (RequestAuthLogin_fn)GetProcAddress(hEngineModule, "?RequestAuthLogin@UNetworkHandler@@UAEHPAG0H@Z");

        RequestLogin(L"admin", L"admin");
    }

}
 
Ну то есть стоит всегда проверять, что возвращают нативные функции апи винды, дабы понимать что пошло не так и на каком этапе
C++:
{
    HMODULE hEngineModule = GetModuleHandleW(L"engine.dll");

    if (hEngineModule == nullptr) {
        return MessageBox(NULL, L"hEngineModule is null", L"", MB_OK);
    }

    uintptr_t UNetworkAddress = (reinterpret_cast<uintptr_t>(hEngineModule)) + UNetworkOffset;

    UNetwork = *reinterpret_cast<UNetworkHandler**>(UNetworkAddress);
    
    MessageBox(NULL, L"Все данные собраны вход!!!!!", L"", MB_OK);

    fRequestAuthLogin = (RequestAuthLogin_fn)GetProcAddress(hEngineModule, "?RequestAuthLogin@UNetworkHandler@@UAEHPAG0H@Z");

    if (fRequestAuthLogin == nullptr) {
        return MessageBox(NULL, L"fRequestAuthLogin is null", L"", MB_OK);
    }

    RequestLogin(L"admin", L"admin");
}
 
Ну то есть стоит всегда проверять, что возвращают нативные функции апи винды, дабы понимать что пошло не так и на каком этапе
Обработку ошибок я сразу сделал.
Ваш варант проверю, только так и получится я думаю но надо еще знать что ожидать while
 
знать что ожидать while
смотреть в документации на сайте майков по определенной функции, к примеру возвращает null в случаи неудачи

1730539169738.png
стало быть во while цикле продолжаем итерации пока значение будет не null/nullptr
 
смотреть в документации на сайте майков по определенной функции, к примеру возвращает null в случаи неудачи

Посмотреть вложение 79183
Я не про функции которые я вызываю для них я сделал проверку - Я про процесс и думал может можно перехватить функцию которая сообщает о том что окно готово к работе

òbi


В данный момент я использую Sleep(3000), а затем вызываем функцию входа в игру.
Поскольку у всех разные компьютеры, скорость загрузки окна может быть выше 3000 миллисекунд, и тогда функция не будет выполнена.
 
Я не про функции которые я вызываю для них я сделал проверку - Я про процесс и думал может можно перехватить функцию которая сообщает о том что окно готово к работе

òbi


В данный момент я использую Sleep(3000), а затем вызываем функцию входа в игру.
Поскольку у всех разные компьютеры, скорость загрузки окна может быть выше 3000 миллисекунд, и тогда функция не будет выполнена.
Как вариант использовать костыль, в коде меню с правой стороны например в HF, можно прописать чтобы переключало некий bool когда загружено в самой dll тогда та уже выполнит те функции которые нужны.
Это как один из вариантов, но возникает вопрос что будет если одно окно загружено и второе не загрузилось ведь не все играют в одно окно, многие и по 5-10 окон. :Hot:
 
Я не про функции которые я вызываю для них я сделал проверку - Я про процесс и думал может можно перехватить функцию которая сообщает о том что окно готово к работе

òbi


В данный момент я использую Sleep(3000), а затем вызываем функцию входа в игру.
Поскольку у всех разные компьютеры, скорость загрузки окна может быть выше 3000 миллисекунд, и тогда функция не будет выполнена.
Как и в моем примере, нам нужно из текущего процесса достать MainWindowHandle.

Достать ссылку на текущее окно в теории можно так
C++:
        HWND hCurWnd = nullptr;
        do
        {
            hCurWnd = FindWindowEx(nullptr, hCurWnd, nullptr, nullptr);
            DWORD checkProcessID = 0;
            GetWindowThreadProcessId(hCurWnd, &checkProcessID);
            if (checkProcessID == GetCurrentProcessId())
            {
                //
            }
        } while (hCurWnd != nullptr);
        
        // OR

        HWND hWnd;
        for (;;)
        {
            DWORD a;
            GetWindowThreadProcessId((hWnd = GetActiveWindow()), &a);
            if ((a != GetCurrentProcessId()))
            {
                hWnd = NULL;
                continue;
            }
        }

Рабочих вариантов не будет, ибо не пишу на плюсах и познания в них у меня околонулевые.
 
Как и в моем примере, нам нужно из текущего процесса достать MainWindowHandle.
Активное загруженное окно можно получить и так HWND GetForegroundWindow();
Пока ищу способ может кто-то еще подскажет)

Учтите что UNetworkHandler при аттаче библиотеки может не существовать вовсе. Запрашивать RequestAuthLogin нужно после того как клиент загрузился (окно логин-пароль): проще всего перехватывать первый вызов UNetworkHandler::Tick() (?Tick@UNetworkHandler@@UAEXM@Z)
Попустил твое сообщение подскажи как использовать UNetworkHandler::Tick() (?Tick@UNetworkHandler@@UAEXM@Z) Из него нужно получить какие-то параметры?
 
У меня возник вопрос, связанный с этой же темой.
Есть ли какой-то флаг в L2, который позволяет определить, что окно с интерфейсом полностью загружено и готово к использованию?
Попробуй через класс окна l2UnrealWWindowsViewportWindow
 
Попробуй через класс окна l2UnrealWWindowsViewportWindow
Для меня не составляет труда найти окно, сложнее понять, когда оно загружено полностью или, по крайней мере, готово к обработке команд от сервера.
 
Пока я сделал так: файл fire.dll загружается непосредственно перед тем, как окно будет готово.

C++:
// Поток для авторизации
static DWORD WINAPI TestThread(LPVOID lpParameter) {

    // Получение модуля fire.dll
        HMODULE fireStart = GetModuleHandleW(L"fire.dll");
 
    // Цикл, который выполняется, пока не будет получен handle
    while (!fireStart) {

        Sleep(100); // 100 миллисекунд

        // Повторно получить handle
        fireStart = GetModuleHandleW(L"fire.dll");

    }

    Sleep(1000);// 1000 миллисекунд

// Вызов функции авторизации
RequestLogin(login, password);

    return 0;
}
 
Активное загруженное окно можно получить и так HWND GetForegroundWindow();
Пока ищу способ может кто-то еще подскажет)


Попустил твое сообщение подскажи как использовать UNetworkHandler::Tick() (?Tick@UNetworkHandler@@UAEXM@Z) Из него нужно получить какие-то параметры?
У клиента есть функция проверки состояния, которая активно используется в nwindow.dll - UL2ConsoleWnd::GetState
1730555411222.png
Можно воспользоваться этим знанием вот так:
C++:
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 клиента
}

DWORD WINAPI init(LPVOID lpParameter)
{
    HMODULE hNwindowModule = nullptr;
    while (hNwindowModule == nullptr)
    {
        hNwindowModule = GetModuleHandleW(L"nwindow.dll");
        Sleep(1000);
    }

    // получение валидного указателя на UL2ConsoleWnd
    uintptr_t pUL2ConsoleWnd = (reinterpret_cast<uintptr_t>(hNwindowModule)) + consoleOffset;
    while (UL2ConsoleWndPtr == nullptr)
    {
        UL2ConsoleWndPtr = *reinterpret_cast<UL2ConsoleWnd**>(pUL2ConsoleWnd);
        Sleep(300);
    }

    // получение адреса по которому записывается текущее состояние
    L2ConsoleState* statePtr = reinterpret_cast<L2ConsoleState*>(UL2ConsoleWndPtr + 0x38);

    // просто пример получения текущего состояния в цикле
    // можно сделать отдельной функцией и получать состояние по необходимости
    while (true)
    {
        L2ConsoleState currentState = *statePtr;
        switch (currentState)
        {
        case L2ConsoleState::Loading:
            MessageBoxW(NULL, L"загрузка", L"L2ConsoleState", MB_OK);
            break;
        case L2ConsoleState::Login:
            MessageBoxW(NULL, L"лобби", L"L2ConsoleState", MB_OK);
            break;
        case L2ConsoleState::CharCreate:
            MessageBoxW(NULL, L"cоздание чара", L"L2ConsoleState", MB_OK);
            break;
        case L2ConsoleState::CharSelect:
            MessageBoxW(NULL, L"выбор чара", L"L2ConsoleState", MB_OK);
            break;
        case L2ConsoleState::InGame:
            MessageBoxW(NULL, L"в игре", L"L2ConsoleState", MB_OK);
            break;
        }
        Sleep(5000);
    }
    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        DisableThreadLibraryCalls(hModule);
        {
            HANDLE hThread = CreateThread(NULL, 0, init, NULL, 0, NULL);
            if (hThread != NULL)
            {
                CloseHandle(hThread);
            }
        }
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
 
Вот код автологина для interlude, который требует доработки, но уже работает, особенно благодаря Charmant

Логин и пароль берется из файла AutoLogin.ini который должен лежать рядом с dll в вашей папке system игры.

AutoLogin.ini с таким содержимым:
Код:
[AutoLogin]
Login=admin
Password=admin
Код на с++ Visual Studio 2022
C++:
#include "pch.h"
#include <windows.h>
#include <string>

extern "C" void __declspec(dllexport) __stdcall function1()
{
    //Заглушка для экспорта L2.exe (От крита клиента!)
}

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 клиента
}

// Определение класса UNetworkHandler
class UNetworkHandler {};

// Тип функции RequestAuthLogin
typedef int(__fastcall* RequestAuthLoginFn)(UNetworkHandler*, int, const wchar_t*, const wchar_t*, int);

// Смещение для UNetworkHandler в engine.dll
const uintptr_t unetworkOffset = 0x81F538;

// Глобальные переменные для хранения указателей
UNetworkHandler** unetwork = nullptr;
RequestAuthLoginFn requestAuthLoginFn = nullptr;

// Функция для авторизации
static void RequestLogin(const std::wstring& login, const std::wstring& password) {
    // Проверка инициализации указателей
    if (!unetwork || !requestAuthLoginFn) {
        return;
    }
    // Вызов функции авторизации из игры
    requestAuthLoginFn(*unetwork, 0, login.c_str(), password.c_str(), 0);
}

// Функция для чтения строки из INI-файла
static void ReadIniString() {
    std::wstring login;
    std::wstring password;
    wchar_t buffer[256] = { 0 };
    // Чтение логина и пароля из ini-файла
    GetPrivateProfileStringW(L"AutoLogin", L"Login", L"", buffer, _countof(buffer), L".\\AutoLogin.ini");
    login = buffer;
    // Чтение логина и пароля из ini-файла
    GetPrivateProfileStringW(L"AutoLogin", L"Login", L"", buffer, _countof(buffer), L".\\AutoLogin.ini");
    password = buffer;

    // Вызов функции авторизации
    RequestLogin(login, password);
}

static void L2StatusLoad() {

    HMODULE hNwindowModule = nullptr;
    while (hNwindowModule == nullptr)
    {
        hNwindowModule = GetModuleHandleW(L"nwindow.dll");
        Sleep(1000);
    }

    // получение валидного указателя на UL2ConsoleWnd
    uintptr_t pUL2ConsoleWnd = (reinterpret_cast<uintptr_t>(hNwindowModule)) + consoleOffset;

    while (UL2ConsoleWndPtr == nullptr)
    {
        UL2ConsoleWndPtr = *reinterpret_cast<UL2ConsoleWnd**>(pUL2ConsoleWnd);
        Sleep(300);
    }

    // получение адреса по которому записывается текущее состояние
    L2ConsoleState* statePtr = reinterpret_cast<L2ConsoleState*>(UL2ConsoleWndPtr + 0x38);

    while (true)
    {
        L2ConsoleState currentState = *statePtr;
        switch (currentState)
        {
        case L2ConsoleState::Loading:
            //    MessageBoxW(NULL, L"загрузка", L"L2ConsoleState", MB_OK);
            ReadIniString();
            exit;
            break;
        case L2ConsoleState::Login:
            //MessageBoxW(NULL, L"лобби", L"L2ConsoleState", MB_OK);
            break;
        case L2ConsoleState::CharCreate:
            //MessageBoxW(NULL, L"cоздание чара", L"L2ConsoleState", MB_OK);
            break;
        case L2ConsoleState::CharSelect:
            //MessageBoxW(NULL, L"выбор чара", L"L2ConsoleState", MB_OK);
            break;
        case L2ConsoleState::InGame:
            //    MessageBoxW(NULL, L"в игре", L"L2ConsoleState", MB_OK);
            break;
        }
        Sleep(5000);
    }
}

// Поток для авторизации
static DWORD WINAPI TestThread(LPVOID lpParameter) {
    L2StatusLoad();
    return 0;
}


// Точка входа DLL
extern "C" __declspec(dllexport)
 BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved) {
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        // Отключение отслеживания вызовов функций библиотеки
        DisableThreadLibraryCalls(hModule);

        // Получение модуля engine.dll
        HMODULE engineModule = GetModuleHandleW(L"engine.dll");
        if (engineModule) {
            // Получение адреса UNetworkHandler
            unetwork = reinterpret_cast<UNetworkHandler**>(reinterpret_cast<uintptr_t>(engineModule) + unetworkOffset);

            // Получение адреса функции RequestAuthLogin
            requestAuthLoginFn = (RequestAuthLoginFn)GetProcAddress(engineModule, "?RequestAuthLogin@UNetworkHandler@@UAEHPAG0H@Z");
        }

        // Создание потока для авторизации
        CreateThread(nullptr, 0, TestThread, nullptr, 0, nullptr);
    }
    return TRUE;
}

И вот готовые файлы для тех, кто хочет настроить автологин (*Только вход в окно логина*): просто распакуйте архив в системную папку игры interlude и не забудьте изменить логин и пароль в файле AutoLogin.ini.
 

Вложения

  • Aurologin.zip
    6,5 КБ · Просмотры: 33
Назад
Сверху Снизу