PDA

Просмотр полной версии : [Руководство] Работаем с хештейблами на примере мобов вокруг нас


jdark
28.07.2011, 14:54
На примере написанной мной программки
xCube ([Ссылки могут видеть только зарегистрированные и активированные пользователи]).
Смещения ру офф клиента можно взять тут ([Ссылки могут видеть только зарегистрированные и активированные пользователи])

Написана на Codegear C Builder 2007 ( впрочем от 6 билдера до XE, особо разницы нету...с юникодом ток немного)

4Zhyk.ru ([Ссылки могут видеть только зарегистрированные и активированные пользователи]*********/fd15774)

Описание проекта: на форме у нас 4 компонента: PopupMenu, ImageList, Timer, TrayIcon.

Итак: чего нам надо сделать
1. Посчитать шарики в 5, 27 комнатах
2. Смотреть в таргет на предмет наличия гусениц.

Что для этого потребуется: Смотреть свой таргет, заглянем в смещения
GAME_ADD = A5BFCC;

Структура игрока:
GA +20: HostPlayer Struct
+B0C; -Target


Смотреть окружающих нас мобов:
GA +8 +24 +14: - Mobs count

Структура мобов
GA +8 +24 +18 +[I*4] + (+0)^J +4: (I in [0..300 hex])
+11C MobWorldID
+120 MobID

Как нам надо читать: если хотим прочитать кто у нас в таргете, смотрим выше описанные смещения и считываем 4 бафта по смещению GA, далее по полученному смещению +20, далее по полученному + B0C - получили таргет.

С мобами интересная вещь, самому лень было сразу осиливать))
там где +0 это значит считать в это адресе адрес след. элемента

(+0)^J это наши страницы памяти

в итоге у нас получится такая охинея
1-я страница GA +8 +24 +18 +[I*4] +4:
2-я страница GA +8 +24 +18 +[I*4] +0 +4:
2-я страница GA +8 +24 +18 +[I*4] +0 +0 +4:
и так далее, особо останавливаться не будем, по ходу поймем :)

а теперь вгрыземся в код
смотрим комменты после // дабы оставить код рабочим


//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

DWORD dwPid=0;
HANDLE hProc;
int iRed, iWhite, iBlue;
int iGA = 0xa5bfcc; // Гейм Адресс

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Exit1Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Form1->Hide(); // прячемся в трей
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
// собственно по таймеру, у меня он кажется установле в 400мс, запускаем основную обработку

// объявляем переменные, замечу борланд как то не адекватно читает память если не инизиализировать переменные заранее

DWORD dwPlCount=0;
DWORD dwMass=0, dwPlId=0;
DWORD dwWid=0;
DWORD dwTarget=0;
iRed=0; iWhite=0; iBlue=0;

//Ищем окошко с названием Perfect World и получаем его хенлд
HWND hHandle = FindWindow("ElementClient Window","Perfect World");
//По хэндлу получаем индефикатор процесса, чтобы читать память
GetWindowThreadProcessId(hHandle, &dwPid);

//открываем выбранный процесс
hProc = OpenProcess(0xfff, false, dwPid);
if (hProc) // проверяем открыли
{
DWORD dwTemp = 0;
DWORD dwFind = 0;

//----------------------читаем таргет GA +20 +B0C
ReadProcessMemory(hProc, (LPVOID) iGA, &dwTemp, 4, NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x20), &dwTarget, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTarget + 0xb0c), &dwTarget, 4 , NULL);
//---------------------------------------------------------

//-------------------------читаем структуру мобов GA + 20 мы уже считали в dwTemp, дальше +8 +24
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x8), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x24), &dwTemp, 4 , NULL);
//------------------------------- в dwTemp GA +20 +8 +24, и считываем +14 это таргет, а GA +20 +8 +24 +18 это начало массива
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x14), &dwPlCount, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x18), &dwMass, 4 , NULL);

//-----------самая порнуха [I*4] + (+0)^J +4: (I in [0..300 hex])
// делаем это в 2х циклах, переменная i это J
// ii это I, 300 в 16тиричной это 768
for (int i = 0; i < 5; i++) {

for (int ii = 0; ii < 768; ii++)
{
//считали GA +8 +24 +18 +[I*4]
ReadProcessMemory(hProc, (LPVOID) (dwMass + ii*4), &dwTemp, 4 , NULL);

// тут пошел разбор страниц
switch (i){
case 1:
//1-я страница GA +8 +24 +18 +[I*4] +4:

ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
break;
case 2:
//2-я страница GA +8 +24 +18 +[I*4] +0 +4: и далее...


ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
break;
case 3:
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
break;
case 4:
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
break;
case 5:
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
break;
default:
;}
// и вот почти пройдена наша 3х этажная хнень)) дочитываем +4 до GA +8 +24 +18 +[I*4] + (+0)^J +4 . тут мы добрались уже до самой ячейки структуры моба
ReadProcessMemory(hProc, (LPVOID) (dwTemp+4), &dwTemp, 4 , NULL);

// мда, названа dwPlId, писал для игроков :) вобщем не пугаемся это айдишка моба GA +8 +24 +18 +[I*4] + (+0)^J +4 +120 MobID
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x120), &dwPlId, 4 , NULL);

// если в записи чтото присутствует читаем и WorldID моба GA +8 +24 +18 +[I*4] + (+0)^J +4 +11C MobWorldID

if (dwPlId > 0){
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x11c), &dwWid, 4 , NULL);
dwFind++;
// тут уже непосредственно реализация программы, если в нашем таргете попался моб с идентичным WID, смотрим кто это
// если 18710 значит это злая гусеница, красим трей иконку в красный цвет
// если 18711 в зеленый, ну и если не гусеницы то в серенький
if (dwTarget == dwWid){
switch (dwPlId) {
case 18710:TrayIcon1->IconIndex = 1;break;
case 18711:TrayIcon1->IconIndex = 2;break;
default: TrayIcon1->IconIndex = 0;
;}}

// дальше считаем сферки в 5 или 27 комнате
// просто инкрементируем счетчик, по названию переменной думаю понятно где какой шарик
switch (dwPlId) {
case 18716: iRed++; break;
case 18717: iWhite++; break;
case 18718: iBlue++; break;
default:
;}

dwPlId=0;
}
// если мы прочитанное количество мобов совпало с GA +8 +24 +14: - Mobs count останавливаем циклы, дабы не гонять память в пустую
if (dwFind == dwPlCount) break;
}
if (dwFind == dwPlCount) break;
}
};

}
//---------------------------------------------------------------------------
void __fastcall TForm1::TrayIcon1Click(TObject *Sender)
{
ShowMessage(
"Красных: " + IntToStr(iRed) + "\n\r" +
"Белых: " + IntToStr(iWhite) + "\n\r" +
"Синих: " + IntToStr(iBlue));
}
//---------------------------------------------------------------------------




Ну вот)) критикуйте)) будем добавлять править и все такое

whoami
28.07.2011, 17:08
jdark, это как раз к вопросу о том, что "программа написанная на Borland Builder ... работает пошустрее [чем под .NET]". На самом деле, не так важно на чём пишут программу, важно как это делают. Твой алгоритм обхода списка мобов - просто жутко не оптимальный по времени исполнения, громоздкий, да ещё в редких случаях обходит не всех мобов. Правда, для задачи подсчёта сфер в кубе это не так критично, а вот если ты варкланов на згд ищешь, такой обход списка никуда не годится.

На самом деле, нет там никаких "страниц памяти", есть массив односвязных списков. Их и надо обходить по очереди. Правильный обход списка есть в PWFrameWork:
[Ссылки могут видеть только зарегистрированные и активированные пользователи]

jdark
29.07.2011, 09:57
я про этот метод в курсе, просто пока некогда на билдере повторить
но я так понимаю говоря про оптимальность ты хотел сказать только про гусениц, для подсчета шариков все равно прийдется пробежаться по всему массиву (а хотяяяя...да, ты же знаешь количество мобов, то и читать память меньше)
присылай свой вариант решения ;)

whoami
29.07.2011, 10:40
jdark, нет, подсчёт у тебя тоже кривой. Одни и те же ячейки считываются из памяти много раз. Переписывать на С++ обход списка мне лень, а на C# код уже есть, я ссылку дал. На словах тоже объяснил, что надо сделать: считать сразу весь массив по GA +8 +24 +18, сразу все 0х300 элементов, потом по отдельности пройти до конца все эти 0х300 списков (большинство из них будут пустыми или из 1 элемента).

jdark
29.07.2011, 10:52
идею понял :)
как нибудь сделаю

кстати поч думаешь что одни и теже много раз?
линейно пробегает последовательно, повторов там нету

единственно ты правильно сказал что лучше сделать 1 чтение страницы, а потом ее разобрать чем считывать по 4 байта

jdark
21.08.2011, 05:57
Сделал по аналогии с игроками рядом))) оптимизировал на сколько хватило фантазии ))


DWORD dwPlCount=0;
DWORD dwGuild=0;
DWORD dwMass=0, dwPlId=0, name = 0 ;

ListView1->Clear();
WideChar wcName [64];

dwPid = dwFindClient[ComboBox1->ItemIndex];


if (ComboBox1->ItemIndex > -1 )
{
hProc = OpenProcess(0xfff, false, dwPid);
if (hProc)
{
DWORD dwTemp = 0;
DWORD dwFind = 0;

ReadProcessMemory(hProc, (LPVOID) iGA, &dwTemp, 4, NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x8), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x20), &dwTemp, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x14), &dwPlCount, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x18), &dwMass, 4 , NULL);

Edit1->Text = dwPlCount;

ReadProcessMemory(hProc, (LPVOID) (dwMass), &str, sizeof(str), NULL);
for (int ii = 0; ii < 768; ii++)
{
for (int i = 1; i < 5; i++)
{
if (str[ii])
{
dwTemp = str[ii];
switch (i){
case 5:
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
case 4:
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
case 3:
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
case 2:
ReadProcessMemory(hProc, (LPVOID) (dwTemp), &dwTemp, 4 , NULL);
case 1:
ReadProcessMemory(hProc, (LPVOID) (dwTemp+4), &dwTemp, 4 , NULL);
default:;}

if (dwTemp) {
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x460), &dwPlId, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x5E4), &dwGuild, 4 , NULL);

ReadProcessMemory(hProc, (LPVOID) (dwTemp + 0x618), &name, 4 , NULL);
ReadProcessMemory(hProc, (LPVOID) (name), &wcName, 64 , NULL);

dwFind++;

ListItem = ListView1->Items->Add();
ListItem->Caption = IntToStr(dwFind);
ListItem->SubItems->Add(dwPlId);
ListItem->SubItems->Add(wcName);
ListItem->SubItems->Add(dwGuild);
}
if (dwFind == dwPlCount) break;
}
}
if (dwFind == dwPlCount) break;

}

} else Label1->Caption = Time().TimeString() + " Bind fail";
}



}

whoami
22.08.2011, 13:12
jdark, меня всегда удивляет код типа

for(int i=0; i<10; ++i)
switch(i)
{
case 0: ...
case 1: ...
case 2: ...
...
}

Чем-то подобным меня "радовали" мои студенты-первокурсники, но они не рвались писать ботов для онлайн-игр, гайды по этому делу, и даже по-моему курсовые сами не писали, а скачивали из интернетов :D
Но таки да, эта версия будет работать гораздо шустрее.

jdark
22.08.2011, 17:52
хз)))) вроде как раз удачное применение ))) на самом деле ни когда без бряков не использовал свитч, и даж попадался на ошибки изза этого))) но этот случай точно для него

а так дальше уже хз куда оптимизировать))) с линейным списком разве что ковыряться

388672
24.08.2013, 19:12
На C#, только с именами окружающих мобов

for (int i = 0; i < 5; i++)
{

for (int ii = 0; ii < 768; ii++)
{
var mob = MyPersonage.memory.ChainReadInt32(PWOffssAndAddrss .base_address);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x1c);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x1c);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x24);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x18);

mob = MyPersonage.memory.ChainReadInt32(mob + ii * 0x4);
switch (i)
{
case 1:
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
break;
case 2:
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
break;
case 3:
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
break;
case 4:
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
break;
case 5:
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x0);
break;
default:
break;
}

mob = MyPersonage.memory.ChainReadInt32(mob + 0x4);
mob = MyPersonage.memory.ChainReadInt32(mob + 0x260);
string mob_name = MyPersonage.memory.ChainReadString_Unicode(mob + 0x0, 80);
listBox1.Items.Add(new ListBoxItem() { Content = mob_name });

}
}