Jump to content

Search the Community

Showing results for tags 'HEX'.

More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


  • Russian Section
    • Новости и объявления
    • Пиратия: Документация
    • Пиратия: Релизы
    • Пиратия: Разработка
    • Пиратия: Web
    • Пиратия: Помощь
    • Совместные проекты / набор команды
    • Доска объявлений
    • Программирование
    • Оффтопик
    • Корзина
  • English Section
    • News & Announcements
    • Guides
    • Releases
    • Development
    • Web
    • Questions & Help
    • Shared Projects / Team search
    • Paid services & Requests
    • Programming
    • Offtopic
    • Recycle bin
  • Portuguese Section
    • Dúvidas & Ajuda
  • Spanish Section
    • Preguntas y Ayuda
  • Servers
    • Russian servers
    • English servers

Find results in...

Find results that contain...

Date Created

  • Start


Last Updated

  • Start


Filter by number of...


  • Start



Found 13 results

  1. Duduf

    Fastpanel tricks

    Maybe you know what GameDB.character.skillbag field have 36 slots for fastpanel anchors. But in Game.exe you can use only 16 (by default F1-F8 x2 states) or 24 (with fastfrm plugin F1-F12 x2 states) slots. Now I'll show a few tricks which allow to expand functionality of the fastpanel. This is tested in version 1.3x and should also work in version 2.x. Open the Game.exe in HEX-editor to make changes. Yes all tricks only client-side. Trick 1. Enable third state Find 83 FD 02 56 57 8B D9 7C 04 33 ED EB 09 85 ED 7D Replace with 83 FD 03 56 57 8B D9 7C 04 33 ED EB 09 85 ED 7D Now you can switch 3 states instead of 2 and fill up all 36 slots. Trick 2. Enable F10-F11 keys Compatible with the trick 1 Find 83 FF 08 7F 3A 83 3D D0 A2 66 00 FF 75 31 8B 86 Replace with 83 FF 0A 7F 3A 83 3D D0 A2 66 00 FF 75 31 8B 86 Trick 3. Enable F10-F12 keys Compatible with the trick 1 Find 83 FF 08 7F 3A 83 3D D0 A2 66 00 FF 75 31 8B 86 Replace with 83 FF 0B 7F 3A 83 3D D0 A2 66 00 FF 75 31 8B 86 Trick 4. Enable Tab key for switch panel states Compatible with the tricks 1, 2, 3 Find 83 FF 7B 74 60 81 FF DD 00 00 00 75 0D 6A 11 FF Replace with 83 FF 09 74 60 81 FF DD 00 00 00 75 0D 6A 11 FF Trick 5. Make two panels for 18 slots (F1-F12 + 6 clickable slots) Use trick 3 and 4 for enable F1-F12 keys and Tab key for switch panels Find 83 FF 0C 72 9B BB 0C 00 00 00 8D 9B 00 00 00 00 Replace with 83 FF 12 72 9B BB 12 00 00 00 8D 9B 00 00 00 00 Find 83 FD 0C 0F 82 77 FF FF FF 83 C3 0C 83 FB 24 0F Replace with 83 FD 12 0F 82 77 FF FF FF 83 C3 12 83 FB 24 0F Find 8D 7E 0C 3B F7 7D 1E 8B 83 18 07 00 00 8B 0C B0 Replace with 8D 7E 12 3B F7 7D 1E 8B 83 18 07 00 00 8B 0C B0 Find 00 7C DD 8D 74 6D 00 C1 E6 02 8D 7E Replace with 00 7C DD 8D 74 ED 00 C1 E6 01 8D 7E Find 8D 0C 40 8D 04 8F 8B 04 82 85 C0 74 18 8B 80 98 Replace with 8D 0C C0 8D 04 4F 8B 04 82 85 C0 74 18 8B 80 98 Find B9 0C 00 00 00 F7 F9 33 C0 89 44 24 0D 89 44 24 Replace with B9 12 00 00 00 F7 F9 33 C0 89 44 24 0D 89 44 24 Open scripts/lua/forms/main.clu and replace with frmFast = UI_CreateForm( "frmFast", FALSE, 432, 82, 290, 557, TRUE, FALSE ) --UI_FormSetHotKey( frmFast, ALT_KEY, HOTKEY_T ) UI_ShowForm( frmFast, TRUE ) UI_SetFormStyle( frmFast, 7 ) UI_AddFormToTemplete( frmFast, FORM_MAIN ) UI_SetIsDrag( frmFast, TRUE ) UI_FormSetIsEscClose( frmFast, FALSE ) imgMain1 = UI_CreateCompent( frmFast, IMAGE_TYPE, "imgMain1", 432, 82, 0, 0 ) UI_LoadImage( imgMain1, "texture/ui/frmfast18.tga", NORMAL, 432, 82, 0, 0 ) fscMainF0 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF0", 32, 32, 5, 44 ) fscMainF1 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF1", 32, 32, 39, 44 ) fscMainF2 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF2", 32, 32, 73, 44 ) fscMainF3 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF3", 32, 32, 107, 44 ) fscMainF4 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF4", 32, 32, 141, 44 ) fscMainF5 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF5", 32, 32, 175, 44 ) fscMainF6 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF6", 32, 32, 209, 44 ) fscMainF7 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF7", 32, 32, 243, 44 ) fscMainF8 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF8", 32, 32, 277, 44 ) fscMainF9 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF9", 32, 32, 311, 44 ) fscMainF10 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF10", 32, 32, 345, 44 ) fscMainF11 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF11", 32, 32, 379, 44 ) fscMainF12 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF12", 32, 32, 107, 5 ) fscMainF13 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF13", 32, 32, 141, 5 ) fscMainF14 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF14", 32, 32, 175, 5 ) fscMainF15 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF15", 32, 32, 209, 5 ) fscMainF16 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF16", 32, 32, 243, 5 ) fscMainF17 = UI_CreateCompent( frmFast, FAST_COMMANG_TYPE, "fscMainF17", 32, 32, 277, 5 ) btnFastUp = UI_CreateCompent( frmFast, BUTTON_TYPE, "btnFastUp", 7, 8, 417, 12 + 39 ) UI_LoadButtonImage( btnFastUp, "texture/ui/Nbotton.tga", 7, 8, 82, 2, TRUE ) UI_SetHint( btnFastUp, "Next (Ctrl+[, Ctrl+], Tab)" ) btnFastDown = UI_CreateCompent( frmFast, BUTTON_TYPE, "btnFastDown", 7, 8, 417, 22 + 39 ) UI_LoadButtonImage( btnFastDown, "texture/ui/Nbotton.tga", 7, 8, 82, 12, TRUE ) UI_SetHint( btnFastDown, "Next (Ctrl+[, Ctrl+], Tab)" ) Download frmfast18.tga from https://drive.google.com/open?id=0B-nyWqNUUQcBc0NKNHkzd3VhWm8 and put it in texture/ui folder Thats all. Enjoy!
  2. Hi Guys, I need lines of GameServer.exe of skilleffect.txt and skillinfo.txt PKO 2.4 @V3ct0r -Thanks for Support
  3. [Mod] Editing the limits of .txt tables With the help of this mod for the "PKODev.NET mod loader" system, you can easily edit the limits for .txt/.bin files of the client and server without the need to edit Game.exe and GameServer.exe in the HEX editor. The limits are edited in the text file pkodev.mod.tablelimit.cfg: areaset = 300 character_lvup = 120 characterinfo = 2500 forgeitem = 12 hairs = 500 int_cha_item = 32 iteminfo = 6000 lifelvup = 1000 saillvup = 1000 shipinfo = 120 shipiteminfo = 500 skilleff = 240 skillinfo = 500 characterposeinfo = 100 chaticons = 100 elfskillinfo = 100 eventsound = 30 itempre = 100 itemrefineeffectinfo = 5000 itemrefineinfo = 20000 itemtype = 100 magicgroupinfo = 10 magicsingleinfo = 100 mapinfo = 100 musicinfo = 500 notifyset = 100 objevent = 10 resourceinfo = 3000 sceneffectinfo = 14000 sceneobjinfo = 800 selectcha = 60 serverset = 100 shadeinfo = 14000 stoneinfo = 100 terraininfo = 100 It is not necessary to write to the file all limits If you need to edit only some of them: iteminfo = 12000 characterinfo = 6000 mapinfo = 500 GameServer.exe tables (13) Game.exe tables (31) Requirements Installed mod loading system for server and client (PKOdev.NET mod loader). Modification information Name: pkodev.mod.tablelimit; Version: 1.0; Author: V3ct0r; Type: for client and server (Game.exe and GameServer.exe); Supported executable .exe files: GAME_13X_0, GAME_13X_1, GAME_13X_2, GAME_13X_3, GAME_13X_4, GAME_13X_5, GAMESERVER_136 and GAMESERVER_138. Installation 1) In the "mods" directory of your server or client, create a "pkodev.mod.tablelimit" folder; 2) Place into it the mod DLL file "pkodev.mod.tablelimit.<client/server>.<x>.dll" for your version of Game.exe or GameServer.exe; 3) Place into it the mod settings file "pkodev.mod.tablelimit.cfg"; 4) Edit the "pkodev.mod.tablelimit.cfg" file at your own discretion - specify the required limit values for each .txt/.bin table file: <table_name> = <value> Download 1) Binary release (.dll); 2) The source code of the mod for Visual Studio 2019 Community (C++). If you encounter any problem, bug or have any questions, then feel free to write in this thread.
  4. [Tweak] Screenshots in one folder Hello friends! In this thread, I want to talk about a small client tweak that will allow you to save screenshots of the gameplay directly in the "screenshot" folder. Originally, the client creates screenshots in the "screenshot\1", "screenshot\2", "screenshot\3" ... "screenshot\N" subfolders, which may not be very convenient for searching and viewing screenshots. Actually, the participant of our forum @dragontechi faced this problem: To fix this inconvenience of storing screenshots, you need to make the following changes to the client: 1) Open the game engine DLL "MindPower3D_D8R.dll" (located in the "system" folder) in any HEX editor, for example, in HxD; 2) Search for the line: screenshot\%d\ and replace it with the following: screenshot\%d_ 3) Next, find the sequence of bytes: E8 83 2C F9 FF and replace them with: 90 90 90 90 90 4) Now find the bytes: C7 85 74 FF FF FF 00 00 00 00 B8 01 00 00 00 85 C0 74 53 8B 85 74 FF FF FF 50 and replace them with: 6A 00 E8 84 E9 16 00 83 C4 04 50 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 5) Save the changes made in the file "MindPower3D_D8R.dll". The result will be the following: All screenshots are now stored in the same "screenshot" folder. Additionally, the title of each screenshot contains the timestamp when it was created (in the context of the time of the operating system on which the client was running at the time the screenshot was created). Thank you for attention!
  5. [Гайд] Пример создания модификации: Вывод дополнительных характеристик персонажа Привет! В данной статье я расскажу как создать мод для клиента игры, который позволит выводить дополнительные характеристики персонажа на форме "Персонаж" (см. скриншот выше). Благодарю участника нашего форума @Graf за идею мода и гайда: Вам понадобится 1) Game.exe для которого будет создаваться модификация; 2) OllyDbg v1.10; 3) IDA Pro (необязательно, можно пользоваться только OllyDbg); 4) Visual Studio 2022 Community Edition и библиотека MS Detours; 5) Исходный код клиента; 6) Знание основ программирования на C/C++ и ассемблере. Навыки написания DLL-библиотек. Опыт работы с вышеуказанными программами. Создание мода В качестве примера реализуем вывод трех дополнительных характеристик: удача (ATTR_LUK ), шанс критического удара (ATTR_CRT ) и шанс дропа (ATTR_MF). 1) Поместим на форму с характеристиками (frmState) персонажа 3 текстовые метки: удача (labLukShow), шанс критического удара (labCriticalShow), шанс дропа (labMfShow). preperty.clu: -- Lucky labLukShow = UI_CreateCompent( frmState, LABELEX_TYPE, "labLukShow", 26, 8, 16, 370 ) UI_SetCaption( labLukShow, "L" ) UI_SetTextColor( labLukShow, COLOR_PURPLE ) UI_SetLabelExFont( labLukShow, DEFAULT_FONT, TRUE, COLOR_WHITE ) -- Critical chance labCriticalShow = UI_CreateCompent( frmState, LABELEX_TYPE, "labCriticalShow", 26, 8, 80, 370 ) UI_SetCaption( labCriticalShow, "C" ) UI_SetTextColor( labCriticalShow, COLOR_PURPLE ) UI_SetLabelExFont( labCriticalShow, DEFAULT_FONT, TRUE, COLOR_WHITE ) -- Drop chance labMfShow = UI_CreateCompent( frmState, LABELEX_TYPE, "labMfShow", 26, 8, 144, 370 ) UI_SetCaption( labMfShow, "M" ) UI_SetTextColor( labMfShow, COLOR_PURPLE ) UI_SetLabelExFont( labMfShow, DEFAULT_FONT, TRUE, COLOR_WHITE ) 2) Изучив исходный код клиента, находим функцию void CStateMgr::RefreshStateFrm(), в которой происходит обновление меток с характеристиками персонажа. Далее нам будет необходимо перехватывать её вызов и обновлять наши новые метки. Так же есть функция void CStateMgr::_evtMainShow(CGuiData *pSender), она вызывается при открытии формы характеристик персонажа и её можно использовать для получения указателей на объекты текстовых меток в памяти игры. Параметр pSender это указатель на форму frmState, объекта класса CCompent, у которого есть метод CCompent* CForm::Find(const char* str), этот метод понадобится для получения указателей на метки по их именам, указанных в lua скриптах GUI клиента. Чтобы установить текст меток, нам будет нужен метод void CLabel::SetCaption(const char * str). Наконец, для доступа к параметрам нашего персонажа, нам будет нужен указатель на персонажа, которым управляет игрок. 3) Для поиска адресов в Game.exe будем использовать программу IDA Pro. void CStateMgr::RefreshStateFrm(). Данный метод можно найти по какой-либо уникальной строке, например, "%4.2f%%". Ищем эту строку в IDA Pro и находим её использование только в одной функции по адресу 0x0047F190 - это и есть искомый метод. void CStateMgr::_evtMainShow(CGuiData *pSender). Это обработчик события открытия формы характеристик персонажа, значит он где-то "вешается" на объект в формы. Проверим в исходном коде клиента игры в методе инициализации формы frmState: bool CStateMgr::Init() { CFormMgr &mgr = CFormMgr::s_Mgr; frmState = _FindForm("frmState"); if( !frmState ) return false; frmState->evtShow = _evtMainShow; labStateName = dynamic_cast<CLabelEx*>(frmState->Find("labStateName")); if( !labStateName ) return Error( g_oLangRec.GetString(45), frmState->GetName(), "labStateName" ); labStateName->SetIsCenter(true); Теперь необходимо найти этот код в Game.exe. Для этого выполняем поиск по строке "frmState". В этот раз нам повезло меньше, найдено 3 использования такой строки: Смотрим первое использование: Рядом так же видно использование строк "btnState", "btnSkill", и "frmSkill". Но в искомом коде таких строк рядом нет. Очевидно, этот адрес нам не подходит. Смотрим второе использование: Строка находится в самом начале функции и рядом есть строка "labStateName". Судя по всему, это наш искомый метод инициализации. Здесь видно, как загружается указатель на форму frmState в регистр EAX, а далее по смещению 0x78, относительно адреса в регистре EAX, помещается какая-то функция. Наверняка, это и есть метод void CStateMgr::_evtMainShow(CGuiData *pSender). Итак, адрес метода void CStateMgr::_evtMainShow(CGuiData *pSender) - 0x0047F7F0. CCompent* CForm::Find(const char* str). Этот метод скорее всего находится в функции инициализации формы, такую мы уже видели: bool CStateMgr::Init(). Просмотрев исходный код, видим следующее: labStateName = dynamic_cast<CLabelEx*>(frmState->Find("labStateName")); if( !labStateName ) return Error( g_oLangRec.GetString(45), frmState->GetName(), "labStateName" ); labStateName->SetIsCenter(true); Это код получения указателя на метку labStateName, нам предстоит сделать то же самое с нашими новыми метками. Идем по адресу 0x0047F9F0 метода bool CStateMgr::Init() в IDA Pro и видим следующий код: .text:0047FA25 push offset aLabstatename ; "labStateName" .text:0047FA2A call dword ptr [eax+48h] Похоже на вызов метода CCompent* CForm::Find(const char* str) для получения указателя на метку labStateName, но его адрес не указан явно. Это говорит о том, что метод виртуальный. Проверим это в исходном коде: virtual CCompent* Find( const char* str ) { return _frmOwn->Find( str ); } Метод действительно является виртуальным. Как же получить его адрес? Как вариант, открыть Game.exe в отладчике, поставить точку останова на адрес 0x0047FA2A и проверить адрес вызываемой функции, но есть проблема - мы не можем запустить клиент напрямую через отладчик, а после запуска эта функция уже не вызывается. Есть второй вариант: поставить точку останова на метод void CStateMgr::_evtMainShow(CGuiData *pSender) (его адрес мы уже знаем), получить адрес формы frmState через параметр pSender, прибавить к нему смещение 0x48 и посмотреть адрес искомого метода. Идем в игру и открываем форму характеристик персонажа: На стеке смотрим адрес параметра pSender - 0x109D5568. По этому адресу находится адрес объекта CStateMgr - 0x006089B4. Прибавляем к этому адресу смещение 0x48, получаем адрес 0x006089FC и переходим по данному адресу в окне отображения памяти процесса: Итак, адрес метода CCompent* CForm::Find(const char* str) равен 0x004941F0. void CLabel::SetCaption(const char * str), в исходном коде вызов этого метода встретится в методе void CStateMgr::RefreshStateFrm(), например: if ( labStateEXP) { if (max!=0) sprintf( pszCha , "%4.2f%%" , num*100.0f/max ); else sprintf( pszCha , "0.00%"); labStateEXP->SetCaption( pszCha ); } Снова ищем строку "%4.2f%%" в IDA Pro и переходим по адресу её использования: Очевидно, что вызов неизвестной функции после двух вызовов библиотечной функции sprintf устанавливает текст метки labStateEXP. Функция является виртуальной, поэтому вновь открываем отладчик, ставим точку останова по адресу 0x0047F2A1 и открываем форму характеристик персонажа в игре: Отладчик подсказывает нам, что по адресу [EDX+3C] находится адрес метода void CLabel::SetCaption(const char * str) - 0x0042B1A0. Указатель на персонажа игрока можно посмотреть в том же методе void CStateMgr::RefreshStateFrm(): CForm * f = g_stUIState.frmState; if( !f->GetIsShow() ) return; CCharacter* pCha = g_stUIBoat.GetHuman(); if( !pCha ) return; SGameAttr* pCChaAttr = pCha->getGameAttr(); if (!pCChaAttr ) return; Переходим на начало метода в IDA Pro: Анализ ассемблерного листинга показывает, что указатель на персонажа игрока находится по адресу 0x00668B6C. Мы нашли все необходимые адреса: 0x0047F7F0 void CStateMgr::_evtMainShow(CGuiData *pSender) 0x0047F190 void CStateMgr::RefreshStateFrm() 0x004941F0 CCompent* CForm::Find(const char* str) 0x0042B1A0 void CLabel::SetCaption(const char * str) 0x00668B6C CCharacter* CBoatMgr::_pHuman 4) Все готово для создания DLL. В качестве языка программирования будем использовать C++. Создаем новый проект Dynamic-Link Library (DLL) в Visual Studio 2022 Community. Поскольку мы будем перехватывать вызовы void CStateMgr::_evtMainShow(CGuiData *pSender) и void CStateMgr::RefreshStateFrm(), то нам понадобится библиотека MS Detours, подключаем её к проекту. Записываем код DLL: #include <windows.h> #include <detours.h> #include <cstdio> namespace pkodev { // Addresses of imported functions from Game.exe namespace address { // void CStateMgr::_evtMainShow(CGuiData *pSender) const unsigned int CStateMgr___evtMainShow = 0x0047F7F0; // void CStateMgr::RefreshStateFrm() const unsigned int CStateMgr__RefreshStateFrm = 0x0047F190; // CCompent* CForm::Find( const char* str ) const unsigned int CForm__Find = 0x004941F0; // void CLabel::SetCaption( const char * str ) const unsigned int CLabel__SetCaption = 0x0042B1A0; // CCharacter* CBoatMgr::_pHuman const unsigned int CBoatMgr___pHuman = 0x00668B6C; } namespace pointer { // void CStateMgr::_evtMainShow(CGuiData *pSender) typedef void(__cdecl* CStateMgr___evtMainShow__Ptr)(void*); CStateMgr___evtMainShow__Ptr CStateMgr___evtMainShow = (CStateMgr___evtMainShow__Ptr)(void*)(address::CStateMgr___evtMainShow); // void CStateMgr::RefreshStateFrm() typedef void(__cdecl* CStateMgr__RefreshStateFrm__Ptr)(void*); CStateMgr__RefreshStateFrm__Ptr CStateMgr__RefreshStateFrm = (CStateMgr__RefreshStateFrm__Ptr)(void*)(address::CStateMgr__RefreshStateFrm); // CCompent* CForm::Find( const char* str ) typedef void* (__thiscall* CForm__Find__Ptr)(void*, const char*); CForm__Find__Ptr CForm__Find = (CForm__Find__Ptr)(void*)(address::CForm__Find); // void CLabel::SetCaption( const char * str ) typedef void (__thiscall* CLabel__SetCaption__Ptr)(void*, const char*); CLabel__SetCaption__Ptr CLabel__SetCaption = (CLabel__SetCaption__Ptr)(void*)(address::CLabel__SetCaption); } namespace hook { // void CStateMgr::_evtMainShow(CGuiData *pSender) void __cdecl CStateMgr___evtMainShow(void* pSender); // void CStateMgr::RefreshStateFrm() void __fastcall CStateMgr__RefreshStateFrm(void* This, void* NotUsed); } // Label "labLukShow" void* labLukShow = nullptr; // Label "labCriticalShow" void* labCriticalShow = nullptr; // Label "labMfShow" void* labMfShow = nullptr; } // Entry point BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { // DLL attached to the proccess case DLL_PROCESS_ATTACH: // Enable hooks DetourRestoreAfterWith(); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)pkodev::pointer::CStateMgr___evtMainShow, pkodev::hook::CStateMgr___evtMainShow); DetourAttach(&(PVOID&)pkodev::pointer::CStateMgr__RefreshStateFrm, pkodev::hook::CStateMgr__RefreshStateFrm); DetourTransactionCommit(); break; // DLL detached from the proccess case DLL_PROCESS_DETACH: // Disable hooks DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)pkodev::pointer::CStateMgr___evtMainShow, pkodev::hook::CStateMgr___evtMainShow); DetourDetach(&(PVOID&)pkodev::pointer::CStateMgr__RefreshStateFrm, pkodev::hook::CStateMgr__RefreshStateFrm); DetourTransactionCommit(); break; } return TRUE; } // void CStateMgr::_evtMainShow(CGuiData *pSender) void __cdecl pkodev::hook::CStateMgr___evtMainShow(void* pSender) { // Get pointers to the labels pkodev::labLukShow = pkodev::pointer::CForm__Find(pSender, "labLukShow"); pkodev::labCriticalShow = pkodev::pointer::CForm__Find(pSender, "labCriticalShow"); pkodev::labMfShow = pkodev::pointer::CForm__Find(pSender, "labMfShow"); // Call original CStateMgr___evtMainShow() function pkodev::pointer::CStateMgr___evtMainShow(pSender); } // void CStateMgr::RefreshStateFrm() void __fastcall pkodev::hook::CStateMgr__RefreshStateFrm(void* This, void* NotUsed) { // Call original function CStateMgr::RefreshStateFrm() pkodev::pointer::CStateMgr__RefreshStateFrm(This); // Get pointer to the main character // CCharacter* pCha = g_stUIBoat.GetHuman(); void* pCha = reinterpret_cast<void*>( *reinterpret_cast<unsigned int*>(pkodev::address::CBoatMgr___pHuman) ); if (pCha == nullptr) { // Exit from the hook return; } // Define ATTR constants const unsigned int ATTR_LUK = 30; const unsigned int ATTR_MF = 38; const unsigned int ATTR_CRT = 39; // Get offset to the attribute auto attr_get = [&pCha](unsigned int id) -> unsigned int { return *reinterpret_cast<unsigned int*>( (0xD0 + 4 * id + reinterpret_cast<unsigned int>(pCha)) ); }; // Buffer for labels captions char buf[128]{ 0x00 }; // Show character's ATTR_LUK if (pkodev::labLukShow != nullptr) { sprintf_s(buf, sizeof(buf), "LUK: %d", attr_get(ATTR_LUK)); pkodev::pointer::CLabel__SetCaption(pkodev::labLukShow, buf); } // Show character's ATTR_CRT if (pkodev::labCriticalShow != nullptr) { sprintf_s(buf, sizeof(buf), "CRT: %d", attr_get(ATTR_CRT)); pkodev::pointer::CLabel__SetCaption(pkodev::labCriticalShow, buf); } // Show character's ATTR_MF if (pkodev::labMfShow != nullptr) { sprintf_s(buf, sizeof(buf), "MF: %d", attr_get(ATTR_MF)); pkodev::pointer::CLabel__SetCaption(pkodev::labMfShow, buf); } } 1. В начале определяем найденные нами в пункте 3 адреса методов, функций и объектов; 2. Далее определяем указатели на необходимые функции и методы; 3. Затем определяем функции-перехватчики и глобальные переменные под указатели на текстовые метки; 4. В функции DllMain() записываем код для установки и снятия перехватчиков с помощью библиотеки Detours; 5. Реализуем перехватчик pkodev::hook::CStateMgr___evtMainShow(). В этой функции получаем указатели на объекты новых меток labLukShow, labCriticalShow и labMfShow с помощью метода CCompent* CForm::Find(const char* str); 6. Реализуем перехватчик pkodev::hook::CStateMgr__RefreshStateFrm(). Сначала получаем указатель на персонажа, которым управляет игрок. Далее записываем константы, обозначающие ID требуемых характеристик. Теперь стоит задача получения этих характеристик из памяти игры. В исходном коде клиента для этого используется указатель на объект типа SGameAttr, связанный с персонажем: SGameAttr* pCChaAttr = pCha->getGameAttr(); if (!pCChaAttr ) return; Анализ ассемблерного листинга показывает, что этот указатель находится по смещению 0xD0 относительно указателя на персонажа: .text:0047F1D2 mov ebx, dword_668B6C . . . .text:0047F1E4 add ebx, 0D0h Посмотрим что представляет собой объект SGameAttr в исходном коде: struct SGameAttr { long lAttr[MAX_ATTR_CLIENT]; // MAX_ATTR_CLIENT = 74 . . . }; Это структура, которая содержит только одно поле - массив из 74 целых чисел по 4 байта каждое, индексы элементов которого соответствует ID характеристик персонажа (см. файл AttrType.lua из скриптов GameServer). Из курса программирования мы знаем, что все элементы массива хранятся в памяти друг за другом. Значит, чтобы получить адрес характеристики персонажа с определенным ID, мы можем воспользоваться следующей формулой: 0xD0 + 4 * <ID> + <Адрес персонажа в памяти> Осталось самое простое: вывести требуемые характеристики персонажа в новые текстовые метки с помощью метода void CLabel::SetCaption(const char * str). 5) Компилируем проект и присоединяем DLL библиотеку к процессу игры. В результате мы получим следующее: После успешного тестирования, процесс создания модификации клиента завершен. Отмечу, что в примере я использовал GAME_13X_2, в Вашем Game.exe найденные адреса могут отличаться. Если у Вас возникли вопросы, то смело задавайте их в этой теме. Ниже прикладываю проект DLL-библиотеки и DLL-библиотеку мода. Скачать Архив с проектом DLL библиотеки модификации для Visual Studio 2022 Community и DLL библиотекой модификации из примера (286 Кб) Также на основе данной статьи был создан полноценный мод для вывода характеристик персонажа, который не ограничивается тремя характеристиками:
  6. Как добавить к крыльям эффект полета Привет! В данной статье я расскажу как сделать, чтобы персонаж летал на обычных крыльях. Вам понадобится: Отладчик OllyDBG; Базовые знания ассемблера; Умение переводить числа из десятичной системы счисления в шестнадцатиричную и обратно; Терпение и внимательность. Для примера, заставим нашего персонажа летать на Ангельских крыльях (ID: 937) 0. Все модификации производятся на стороне клиента. Я буду работать с Game.exe из английского клиента TOP версии 1.38, поэтому, скорее всего, у Вас будут отличаться адреса инструкций; 1. Запустите клиент, откройте OllyDBG и присоединитесь к процессу Game.exe (команда File -> Attach -> В списке процессов выбрать Game.exe -> Attach); 2. Далее перейдите в модуль Game.exe (команда View -> Executable Modules или ALT + E -> В списке модулей выбрать Game.exe); 3. Выполните анализ кода (контекстное меню -> Analysis -> Analyse code или CTRL + A); 4. Найдите функцию, которая отвечает за обновление позы персонажа при перемещении крыльев в 1-ю ячейку инвентаря. Например, с помощью команды контекстное меню -> Search for -> Command или CTRL + F. Затем в окошко поиска вводим: CMP EAX, 80 В моем Game.exe она выглядит следующим образом: 004A34B0 /$ 8B81 0C0D0000 MOV EAX,DWORD PTR DS:[ECX+D0C] 004A34B6 |. 3D 80000000 CMP EAX,80 004A34BB |. 7C 11 JL SHORT 004A34CE 004A34BD |. 3D 8C000000 CMP EAX,8C 004A34C2 |. 7F 0A JG SHORT 004A34CE 004A34C4 |. 3D 87000000 CMP EAX,87 004A34C9 |. 74 03 JE SHORT 004A34CE 004A34CB |. B0 01 MOV AL,1 004A34CD |. C3 RETN 004A34CE |> 32C0 XOR AL,AL 004A34D0 \. C3 RETN Если перевести эту функцию в C-код, то она будет выглядеть примерно так: // Проверка на крылья int CheckWings(int id) { if (id >= 128 && id <= 140 && id != 135) { return 1; // В 1-й ячейке инвентаря крылья перерожденя } return 0; // 1-я ячейка пуста или в ней лежит какой-то другой предмет } Наша с Вами задача заключается в изменении этой функции, а именно условия, где сравнивается ID предмета из 1-ой ячейки инвентаря с ID крыльев. Давайте добавим в него нашли крылья: if (id >= 128 && id <= 140 && id != 135 || id == 937) Тогда наша функция на ассемблере будет выглядеть вот так: ; RET_0 - адрес инструкции XOR AL, AL ; RET_1 - адрес инструкции MOV AL, 1 MOV EAX,DWORD PTR DS:[ECX+D0C] CMP EAX, 0x03A9 ; 0x03A9 = 937 - ID Ангельских крыльев JE SHORT RET_1 ; Прыжок на return 1, если ID == 937 CMP EAX,80 JL SHORT RET_0 ; Прыжок на return 0 CMP EAX,8C JG SHORT RET_0 ; Прыжок на return 0 CMP EAX,87 JE SHORT RET_0 ; Прыжок на return 0 MOV AL,1 ; return 1 RETN XOR AL,AL ; return 0 RETN Здесь мы сталкиваемся с проблемой: новая функция занимает больше байт, чем оригинальная! Что делать? В конце модуля есть так называемый Code Cave - область, заполненная нулевыми байтами. Именно туда мы и поместим нашу новую функцию. Найти эту область очень просто: крутите ползунок в окне кода до тех пор, пока не увидите инструкции вида: DB 00 DB 00 DB 00 DB 00 Итак, помещаем нашу функцию в Code Cave, я поместил ее по адресу 0x005FF850: Обратите внимание, что адреса инструкций RET_0 и RET_1 у меня получились 0x005FF875 и 0x005FF872 соответственно. Не забудьте их исправить в функции. Теперь нужно сделать так, чтобы вместо оригинальной функции вызывалась наша, модифицированная. Есть 2 способа: первый заключается в том, что мы изменяем адрес функции во всех других функциях, которые ее вызывают. Суть второго способа состоит в том, что в начале оригинальной функции мы пишем код, который будет перенаправлять вызов на новую модифицированную функцию. Давайте воспользуемся вторым способом. Идем на начало оригинальной функции (в данном гайде это 0x004A34B0) и пишем следующий код, заменяя оригинальный: jmp <адрес модифицированной функции> retn У меня получилось вот так: Протестируем наши изменения. Зайдите в игру и поместите Ангельские крылья в 1-й слот инвентаря. Персонаж должен взлететь: 5. Как видно из скриншота, значок GM провалился в персонажа, давайте это исправим. Найдите следующий код, например, продолжая искать инструкцию CMP EAX, 80: 004A3F7F . 8B86 0C0D0000 MOV EAX,DWORD PTR DS:[ESI+D0C] 004A3F85 . 3D 80000000 CMP EAX,80 004A3F8A . 7C 16 JL SHORT 004A3FA2 004A3F8C . 3D 8C000000 CMP EAX,8C 004A3F91 . 7F 0F JG SHORT 004A3FA2 004A3F93 . 3D 87000000 CMP EAX,87 004A3F98 . 74 08 JE SHORT 004A3FA2 004A3F9A . D905 30936000 FLD DWORD PTR DS:[609330] 004A3FA0 . EB 06 JMP SHORT 004A3FA8 004A3FA2 > D905 70136000 FLD DWORD PTR DS:[601370] На скриншоте ниже в оранжевой рамке я выделил "ориентиры" - вызовы функций SetYaw() и UpdatePitchYawRoll() из библиотеки MindPower: Как видите, здесь точно такое же условие, как и в прошлой функции, и нам надо его изменить. Обратите внимание на адреса 0x004A3FA2 и 0x004A3F9A: 0x004A3F9A - код, который выполнится при соблюдении условия (в 1-й ячейке инвентаря находятся крылья); 0x004A3FA2 - код, который выполнится, если условие не соблюдается (в 1-й ячейке инвентаря нет крыльев). Так как новое условие будет занимать больше места, чем оригинальное, идем в Code Cave и записываем его. Я запишу по адресу 0x005FF87A: Далее необходимо задействовать новый код. Идем по адресу 0x004A3F85 и выполняем прыжок на новое условие: jmp <адрес нового условия> ; в данном гайде - 0x005FF87A Идем в игру и убеждаемся, что значок GM встал на свое место: 6. Если вы прикажете персонажу перемещаться, то увидите, что он будет бегать, а не летать. Исправим и это. Найдите код, примерно похожий на этот: 004A44FE |. 0F944424 1C SETE BYTE PTR SS:[ESP+1C] 004A4503 |. 3D 80000000 CMP EAX,80 004A4508 |. 7C 46 JL SHORT 004A4550 004A450A |. 3D 8C000000 CMP EAX,8C 004A450F |. 7F 3F JG SHORT 004A4550 004A4511 |. 3D 87000000 CMP EAX,87 004A4516 |. 74 38 JE SHORT 004A4550 004A4518 |. 83FF 01 CMP EDI,1 004A451B |. 74 05 JE SHORT 004A4522 004A451D |. 83FF 04 CMP EDI,4 004A4520 |. 75 07 JNZ SHORT 004A4529 004A4522 |> BF 2A000000 MOV EDI,2A 004A4527 |. EB 27 JMP SHORT 004A4550 004A4529 |> 83FF 05 CMP EDI,5 004A452C |. 74 05 JE SHORT 004A4533 004A452E |. 83FF 06 CMP EDI,6 004A4531 |. 75 07 JNZ SHORT 004A453A 004A4533 |> BF 2B000000 MOV EDI,2B 004A4538 |. EB 16 JMP SHORT 004A4550 004A453A |> 83FF 02 CMP EDI,2 004A453D |. 75 07 JNZ SHORT 004A4546 004A453F |. BF 2C000000 MOV EDI,2C 004A4544 |. EB 0A JMP SHORT 004A4550 004A4546 |> 83FF 10 CMP EDI,10 004A4549 |. 75 05 JNZ SHORT 004A4550 004A454B |. BF 2D000000 MOV EDI,2D 004A4550 |> 83F9 06 CMP ECX,6 На скриншоте я опять оставил "ориентиры": И опять то же самое условие, которое нужно изменить. В принципе, процесс редактирования условия схож с предыдущим пунктом, только у нас другие адреса для положительного и отрицательного исхода: 0x004A4518 - код, который выполнится при соблюдении условия (в 1-й ячейке инвентаря находятся крылья); 0x004A4550 - код, который выполнится, если условие не соблюдается (в 1-й ячейке инвентаря нет крыльев). Точно так же идем в Code Cave и пишем новое условие. Я напишу по адресу 0x005FF8AD: Заставляем программу использовать новый код, вместо старого. Идем по адресу 0x004A4503 и делаем прыжок: jmp <адрес нового условия> ; в данном гайде - 0x005FF8AD Идем в игру и тестируем: Ура! Теперь персонаж летает на Ангельских крыльях! 7. Сохраните проделанные изменения. В контекстном меню выберите команду Copy to executable -> All modifications. На вопрос "Copy selection to executable file?" отвечайте "Copy all". Появится окошко, просто закрывайте его крестиком. Тут программа задаст новый вопрос типа: "Файл Game.exe был изменен! Сохранить изменения?", отвечаете "Да" и в диалоге выбора файла, вводите название для измененного Game.exe. 8. На этом редактирование Game.exe завершено! Зайдите в игру и убедитесь что все работает как задумано. В результате мы с Вами заставили персонажей летать на Ангельских крыльях с ID 937!
  7. [Твик] Скриншоты в одной папке Всем привет! В этой теме я хочу рассказать о небольшом твике клиента, который позволит сохранять скриншоты игрового процесса непосредственно в папке "screenshot". Изначально клиент создает скриншоты в подпапках "screenshot\1", "screenshot\2", "screenshot\3" ... "screenshot\N", что может быть не очень удобно для поиска и просмотра скриншотов. Собственно, с этой проблемой к сообществу и обратился пользователь @dragontechi: Чтобы устранить текущее неудобство хранения скриншотов, необходимо выполнить следующие изменения клиента: 1) Откройте DLL-библиотеку движка игры "MindPower3D_D8R.dll" (находится в папке "system") в любом HEX-редакторе, например, в HxD; 2) Найдите строку: screenshot\%d\ и замените её на следующую: screenshot\%d_ 3) Далее найдите последовательность байт: E8 83 2C F9 FF и замените их на: 90 90 90 90 90 4) Теперь найдите байты: C7 85 74 FF FF FF 00 00 00 00 B8 01 00 00 00 85 C0 74 53 8B 85 74 FF FF FF 50 и замените их: 6A 00 E8 84 E9 16 00 83 C4 04 50 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 5) Сохраните проделанные изменения в файле "MindPower3D_D8R.dll". В итоге получится следующий результат: Все скриншоты теперь хранятся в одной папке "screenshot". Дополнительно, в названии каждого скриншота записывается метка времени, когда он был сделан (в контексте времени системы, на которой был запущен клиент в момент создания скриншота). Благодарю за внимание!
  8. Видимость названий объектов и их жизней без нажатой клавиши Shift Всем привет! В данном гайде я расскажу как сделать отображение названий монстров, NPC и ников персонажей, а также их жизней без нажатой клавиши Shift или взятия в цель: Вам понадобится: 1) Game.exe из папки system Вашего клиента; 2) Отладчик OllyDbg. Я буду работать в версии 1.10. Чтобы сделать данную модификацию, необходимо пропатчить Game.exe. Примечание: В этой статье я показываю лишь принцип того, как произвести такую модификацию. Иными словами, инструкции и их адреса у Вас могут отличаться от тех, что я привожу в гайде. Я буду работать с Game.exe из 1.39 ру. оф. клиента. Итак, нам нужно найти в Game.exe код, который обрабатывает нажатие клавиши Shift. У данной клавиши есть код 16 (0x10 в шестнадцатеричной системе счисления). Также нам известно, что в WinAPI есть функция GetKeyState(int nVirtKey), с помощью которой можно узнать, нажата ли клавиша с кодом nVirtKey. Заходим в игру (на карту) и открываем Game.exe в OllyDBG и начинаем искать вызовы функции GetKeyState(). Функция находится в библиотеке USER32.DLL, ставим на нее Break Point (F2). Break Point сразу же срабатывает. Смотрим на стек и видим что функция была вызвана из библиотеки MindPower3D_D8R.dll. Это библиотека движка игры. Переходим по адресу 0x101290D4 (адрес возврата из функции GetKeyState) и видим следующий код: 101290C0 PUSH EBP 101290C1 MOV EBP,ESP 101290C3 SUB ESP,44 101290C6 PUSH EBX 101290C7 PUSH ESI 101290C8 PUSH EDI 101290C9 MOV DWORD PTR SS:[EBP-4],ECX 101290CC PUSH 10 101290CE CALL DWORD PTR DS:[<&USER32.GetKeyState>> 101290D4 MOVSX EAX,AX 101290D7 AND EAX,0FF00 101290DC POP EDI 101290DD POP ESI 101290DE POP EBX 101290DF MOV ESP,EBP 101290E1 POP EBP 101290E2 RETN Видим, что в качестве параметра GetKeyState() передается 0x10 - код клавиши Shift. Возможно, данная функция по адресу 0x101290C0 проверяет, нажата ли клавиша Shift. Назовем ее IsShiftPress(). Далее смотрим где эта функция вызывается: Ставим Break Point на каждый вызов и видим, что функция IsShiftPress() постоянно вызывается по адресам 0x004CC3E2, 0x004D7DE4 и 0x004D7E41. Начинаем с адреса 0x004CC3E2. Переходим на него и видим следующий код: 004CC3E2 CALL DWORD PTR DS:[IsShiftPress] 004CC3E8 TEST EAX,EAX 004CC3EA MOV EBP,2 004CC3EF JE SHORT 004CC45D 004CC3F1 MOV EAX,DWORD PTR DS:[ESI+124] 004CC3F7 XOR EDI,EDI 004CC3F9 TEST EAX,EAX 004CC3FB JLE 004CC62A 004CC401 MOV EAX,DWORD PTR DS:[6705C8] Если Shift не нажат, то происходит прыжок на адрес 0x004CC45D (TEST EAX,EAX - сравнение регистра EAX с 0). Затираем этот прыжок с помощью инструкций NOP, чтобы данный код выполнялся независимо от того, нажат Shift или нет: 004CC3E2 CALL DWORD PTR DS:[IsShiftPress] 004CC3E8 TEST EAX,EAX 004CC3EA MOV EBP,2 004CC3EF NOP 004CC3F0 NOP 004CC3F1 MOV EAX,DWORD PTR DS:[ESI+124] 004CC3F7 XOR EDI,EDI 004CC3F9 TEST EAX,EAX 004CC3FB JLE 004CC62A 004CC401 MOV EAX,DWORD PTR DS:[6705C8] Переходим в игру и видим, что название монстров/персонажей и их жизни отображаются без нажатого Shift'a, чего мы и хотели добиться. Теперь осталось сохранить изменения в Game.exe. Щелкаем правой кнопкой мыши по коду в отладчике, в контекстном меню выбираем Copy to executable -> All modifications и сохраняем Game.exe.
  9. Отключаем сообщения о погоде Привет! В этом гайде я расскажу каким образом можно убрать сообщения о погоде из системы. Работать будем с GameServer.exe, под понятием "сервер" будет подразумеваться GameServer. Есть 2 способа отключения сообщений о погоде. Первый состоит в том, чтобы вообще убрать погоду с сервера. Второй - произвести некоторые изменения в GameServer.exe, при этом погода останется, но сообщения в систему о ней исчезнут. Способ I - Нет погоды нет сообщений В директории resource сервера находятся папки с картами (garner, magicsea, darkblue, guildwar и так далее). В каждой такой папке лежит файл: картаmonster_conf.lua где карта это название папки, в которой находится данный файл - garner, magicsea, darkblue, guildwar и так далее. В этом файле указываются точки возрождения монстров и области карты, в которых происходят погодные явления. Области карты для погоды определяются функцией AddWeatherRegion(). Например, для Аскарона (garner) погода в файле garnermonster_conf.lua определяется следующим образом: AddWeatherRegion(9, 8, 40, 2434, 2800 , 80, 80) AddWeatherRegion(11, 8, 40, 2247, 2960 , 80, 80) AddWeatherRegion(9, 8, 40, 1190, 1140 , 80, 80) AddWeatherRegion(11, 8, 40, 1160, 1400 , 80, 80) Наша задача заключается в том, чтобы удалить или закомментировать (--) вызовы функции AddWeatherRegion() в файле monster_conf.lua для каждой карты. Таким образом, мы отключим погоду на сервере. После проделанных изменений не забудьте перезагрузить GameServer.exe. Способ II - Патчим GameServer.exe Данный способ уже сложнее, но позволяет сохранить погоду на сервере. Вам понадобится отладчик OllyDbg 1.10 и базовые знания языка Ассемблера. Открываем GameServer.exe в OllyDbg (File -> Attach -> В списке процессов выбрать GameServer.exe). Затем переходим в модуль GameServer (View -> Executable modules -> В списке модулей выбрать GameServer), его адрес (Base) 0x00400000. Далее проанализируйте дизассемблированный код. Для этого в контекстном меню окошка с инструкциями (кодом) выберите Analysis -> Analyse code или нажмите сочетание клавиш Ctrl + A. В итоге мы должны получить следующее: Окно с инструкциями - кодом Далее нужно найти строку с сообщением о погоде. Так как я работаю с GameServer.exe версии 1.38, то данная строка находится в .res файле. Декомпилируем .res файл: На выходе получается текстовый файл. Производим поиск по файлу с помощью ключевого слова "weather" и находим строку: GM_WEATHER_CPP_00001 { "Weather: Current sea{0} {1}nearby will occur{2}" } Это шаблонная строка для сообщения о погоде, которое выводится в системный чат. Видно, что идентификатором этой строки является строка "GM_WEATHER_CPP_00001", её мы и будем искать в GameServer.exe. Для этого в контекстном меню окна с инструкциями выполняем команду Search for -> All refernced text strings. В появившемся окне с текстовыми строками ищем строку "GM_WEATHER_CPP_00001" (контекстное меню -> Search for text -> GM_WEATHER_CPP_00001). Затем кликаем по найденной строке правой кнопкой мыши и выбираем команду Follow in Disassembler (или нажимаем Enter). В результате мы видим, что данная строка используется по адресу 0x0004B1D93: 004B1D8B | PUSH EAX ; /Arg3 004B1D8C | LEA ECX,DWORD PTR SS:[EBP-C8] ; | 004B1D92 | PUSH ECX ; |Arg2 004B1D93 | PUSH 0x005C2AA8 ; |Arg1 = 005C2AA8 ASCII "GM_WEATHER_CPP_00001" 004B1D98 | MOV ECX,0x0071E084 ; | 004B1D9D | CALL 0x0054EA70 ; \?Format@CResourceBundleManage@@QAEHPBDAAVCFormatParameter@@QAD@Z 004B1DA2 | LEA EAX,DWORD PTR SS:[EBP-C0] 004B1DA8 | PUSH EAX ; /Arg1 004B1DA9 | MOV ECX,DWORD PTR DS:[71E078] ; | 004B1DAF | CALL 0x0049E0C0 ; \CGameApp::LocalNotice 004B1DB4 | MOV EAX,DWORD PTR SS:[EBP-14] 004B1DB7 | XOR EDX,EDX 004B1DB9 | MOV ECX,3E8 Как нетрудно догадаться, данные инструкции загружают шаблонную строку из .res файла, подставляют в неё координаты и название погодного явления, а затем выводят получившееся сообщение в системный чат. Отключим вывод сообщения о погоде в системный чат. Для этого необходимо затереть соответствующие инструкции: 004B1DA2 | LEA EAX,DWORD PTR SS:[EBP-C0] 004B1DA8 | PUSH EAX ; /Arg1 004B1DA9 | MOV ECX,DWORD PTR DS:[71E078] ; | 004B1DAF | CALL 0x0049E0C0 ; \CGameApp::LocalNotice Чтобы затереть данные инструкции, нужно заменить их байты на 0x90 (инструкция NOP). Выделяем инструкции начиная с адреса 0x04B1DA2 по адрес 0x004B1DAF и вызываем контекстное меню. В контекстном меню выполняем команду Binary -> Fill with NOP's. Получаем следующие инструкции: 004B1D8B | PUSH EAX ; /Arg3 004B1D8C | LEA ECX,DWORD PTR SS:[EBP-C8] ; | 004B1D92 | PUSH ECX ; |Arg2 004B1D93 | 0x005C2AA8 ; |Arg1 = 005C2AA8 ASCII "GM_WEATHER_CPP_00001" 004B1D98 | MOV ECX,0x0071E084 ; | 004B1D9D | 0x0054EA70 ; \?Format@CResourceBundleManage@@QAEHPBDAAVCFormatParameter@@QAD@Z 004B1DA2 | NOP 004B1DA3 | NOP 004B1DA4 | NOP 004B1DA5 | NOP 004B1DA6 | NOP 004B1DA7 | NOP 004B1DA8 | NOP 004B1DA9 | NOP 004B1DAA | NOP 004B1DAB | NOP 004B1DAC | NOP 004B1DAD | NOP 004B1DAE | NOP 004B1DAF | NOP 004B1DB0 | NOP 004B1DB1 | NOP 004B1DB2 | NOP 004B1DB3 | NOP 004B1DB4 | MOV EAX,DWORD PTR SS:[EBP-14] 004B1DB7 | XOR EDX,EDX 004B1DB9 | MOV ECX,3E8 Сохраним проделанные изменения. В контекстном меню выбираем команду Copy to executable -> All modifications. На вопрос "Copy selection to executable file?" отвечаем "Copy all". Появится окошко, просто закрываем его крестиком. Далее программа задаст новый вопрос типа: "Файл GameServer.exe был изменен! Сохранить изменения?", отвечаем "Да" и в диалоге выбора файла, вводим название для измененного GameServer.exe. Запускаем измененный GameServer.exe и убеждаемся, что сообщений о погоде в системе больше нет. Для GameServer.exe версии 1.36 шаг с декомпиляцией .res соответственно нужно пропустить и сразу искать непосредственно строку сообщения о погоде по ключевому слову "weather". Я нашел такую строку: Weather: %s has occurred at location %d %d Затем нужно аналогичным образом затереть инструкции начиная с адреса 0x004AA928 по адрес 0x004AA935.
  10. Hello, Someone who works with Hex, could contact me? I am in search of these services: * Remove the limit of apparel fusion or change the IDs. Please, send message to me. I Pay for services, thank you!
  11. "Вы исчерпали число попыток, игра будет принудительно закрыта" Всем привет! Как известно, если 4 раза подряд ввести неверный пароль от Вашего аккаунта (или аккаунта Вашего друга, который забыл Вам сообщить пароль), то игра закроется с сообщением "Вы исчерпали число попыток, игра будет принудительно закрыта". Иногда это бывает неудобно. В данной статье я расскажу как сделать так, чтобы игра больше не закрывалась. Работать будем с 1.3х ру. оф. клиентом. Сразу скажу, что в данный момент я сам не знаю как это можно сделать, поэтому будем разбираться вместе. С чего начать? Давайте попробуем зацепиться за сообщение "Вы исчерпали число попыток, игра будет принудительно закрыта". Где оно может находиться? Либо в Game.exe, либо в StringSet.txt. Проверим StringSet.txt. Действительно, здесь есть такая строка: [251] "Вы исчерпали число попыток, игра будет принудительно закрыта" Запомним ее номер 251. Он же 0xFB в шестнадцатеричной системе счисления. Откроем Game.exe в отладчике OllyDBG и поищем это число. Нашлось всего 11 инструкций, это хорошая новость. Так же нас интересуют только инструкции PUSH, а их здесь всего 2. Проверим инструкцию push 0xFB по адресу 0x005076A5: Ниже видим вызов системной функции SendMessageA с параметром WM_DESTROY. Что делает функция SendMessageA()? Она отправляет заданное сообщение окну. А заданное сообщение у нас WM_DESTROY, это сообщение посылается когда необходимо уничтожить окно. Таким образом, можно сделать вывод, что этот код закрывает игру, а по адресу 0x005076A5 начинает загружается строка с сообщением. Перестаем гадать и ставим точку останова по адресу 0x005076A5, идем в игру и 4 раза вводим неверный пароль. Точка останова сработала! Значит где-то выше код, который закрывает клиент, если мы 4 раза введем неверный пароль. Теперь нас интересуют инструкции условных переходов. По адресу 0x00507691 можно увидеть инструкцию cmp EAX, 3. А дальше идет условный переход на адрес 0x00507709, если значение в регистре EAX меньше и не равно 3. Можно предположить, что в EAX попадает количество попыток неудачного входа: EAX = 0 (1 попытка); EAX = 1 (2 попытки); EAX = 2 (3 попытки); EAX = 3 (4 попытки). Давайте это проверим. Ставим точку останова по адресу 0x00507691. И вводим несколько раз неверный пароль. Действительно, если 3 раза ввести неверный пароль, то EAX = 2. Как заставить игру перестать закрываться? Очень просто: условный переход меняем на безусловный: Возвращаемся в игру и пробуем вводить неверный пароль. После 10 попытки становится понятно, что игра не закроется и мы достигли поставленной цели. Сохраняем наши изменения в Game.exe. Так же можно изменять количество попыток, после которых игра закроется. cmp EAX, <число попыток - 1> jmp 0x00507709 * число попыток должно быть в шестнадцатеричной системе счисления Например, инструкция cmp EAX,0A Означает, что игра закроется после 11 попытки. На этом все!
  12. Редактирование горячих клавиш в клиенте на примере Insert Всем привет! Меня спросили как можно изменить кнопку, на которую персонаж садится чтобы восстановить очки жизней и маны (Insert). Соответственно, в этом гайде об этом и пойдет речь. Работать будем с Game.exe из последнего официального русского клиента. В этот раз гадать не будем, а заглянем в исходные коды клиента. Произведем поиск по константе с именем VK_INSERT, которая обозначает код клавиши Insert, и находим следующий код: if ( key == VK_INSERT && !GetMainCha()->IsBoat() ) { CInsertState *seat = dynamic_cast<CInsertState*>(GetMainCha()->GetActor()->GetCurState()); if (seat) { seat->Cancel(); return; } seat = new CInsertState(GetMainCha()->GetActor()); seat->SetIsSend( true ); seat->SetAngle( GetMainCha()->getYaw() ); GetMainCha()->GetActor()->SwitchState(seat); return; } Этот код находится в методе _KeyDownEvent(int key), класса CWorldScene, который вызывается при нажатии игроком какой-либо клавиши на клавиатуре. Если Вы работаете с исходными кодами, то можете просто изменить константу VK_INSERT на константу, обозначающую любую другую клавишу, и скомпилировать клиент. В противном случае придется патчить Game.exe. Итак, запускаем игру и присоединяемся к процессу Game.exe в отладчике OllyDBG. Ищем приведенный код выше. Для этого выполним поиск по всем константам: контекстное меню дизассемблера -> Search for -> All constants. В появившемся окошке вводим код клавиши Insert (0x2D): Появится окно с результатами поиска. Результатов будет очень много, но в первую очередь нас интересуют инструкции CMP. Как узнать какая инструкция нам нужна? Ставим на все инструкции CMP точку останова (Breakpoint). Если сразу после того, как Вы поставили точку останова она сработала, то убираем ее и продолжаем выполнение программы. Далее идем в игру и нажимаем клавишу Insert. Сработала точка останова по адресу 0x004CD308 (у Вас адрес может отличаться): Давайте для примера заменим клавишу Insert на клавишу Space (пробел). Этот пример не совсем удачный, так как данная клавиша используется для ввода пробела в чат. Код клавиши Space: #define VK_SPACE 0x20 Остальные коды клавиш Вы можете найти в заголовочном файле WinUser.h Заменяем инструкцию CMP ESI,2D на CMP ESI,20 Переходим в игру и нажимаем пробел. Теперь персонаж садится на землю и встает по нажатию клавиши Пробел. Сохраняем проделанные изменения. Для тех, кто не умеет пользоваться OllyDBG, но использует Game.exe из данного примера. Откройте Game.exe в любом HEX-редакторе (я буду использовать HxD) и перейдите по адресу 0x000CD30A. Как мы получили этот адрес? 0x000CD30A = 0x004CD308 - 0x00400000 + 2 2 = 2 байта инструкции CMP ESI Замените 0x2D на код интересующей Вас клавиши. Сохраните проделанные изменения. На этом все.
  • Create New...