PDA

Просмотр полной версии : Наставьте на путь истинный


dwa83
22.03.2012, 06:01
Здравствуйте, загорелся идеей написать бота под свои нужды (так как нет идеального бота для всех случаев, как уже на форуме кто-то писал).

Скачал Borland C++ builder 6, решил в ней делать.
Делфи с паскалем уже подзабыл, так как в какой то момент на c++ перепрыгнул (с++ тоже вспоминать придётся, да и знания средненькие).

Решил пока сделать считывание параметров перса.
Вооружившись вот этой темкой
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
этой
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
и на всякий случай этой
[Ссылки могут видеть только зарегистрированные и активированные пользователи]

Полазил пару часов пока допёр как подключаться к процессу и как считывать. В итоге прога считывает значение ХП перса, но почему-то не то значение. Знающие с++, ткните меня в то место где я напортачил, уже не знаю в какую сторону копать.

Вот код с проекта
Этот кусок вначале после подключения библиотек
HWND hwndPW; // хэндл окна PW
DWORD pid; // unsigned long
HANDLE process; // процесс PW

DWORD BA = 0x00B27A04; // базовый адрес
DWORD persA1 = 0x1C; // смещение1 (структура перса)
DWORD persA2 = 0x34; // смещение2 (структура перса)
DWORD hpA = 0x0490; // хп перса

DWORD buff;
DWORD bcount;

int hp;

И этот кусок в обработчике нажатия на кнопку(на форме только кнопка и листбокс)
hwndPW=FindWindow(0,"Perfect World");
GetWindowThreadProcessId(hwndPW,&pid);
process=OpenProcess(PROCESS_ALL_ACCESS,false,DWORD (pid));

ReadProcessMemory(process,&BA,&buff,sizeof(buff),&bcount);
ReadProcessMemory(process,&(buff+=persA1),&buff,sizeof(buff),&bcount);
ReadProcessMemory(process,&(buff+=persA2),&buff,sizeof(buff),&bcount);
ReadProcessMemory(process,&(buff+=hpA),&hp,sizeof(hp),&bcount);

CloseHandle(process);

ListBox1->Clear();
ListBox1->Items->Add("Хэндл окна - "+IntToStr(int(hwndPW)));
ListBox1->Items->Add("PID просесса - "+IntToStr(int(pid)));
ListBox1->Items->Add("просесс - "+IntToStr(int(process)));
ListBox1->Items->Add("Базовый адрес - "+IntToStr(int(BA)));
ListBox1->Items->Add("Что получили - "+IntToStr(int(hp)));


[Ссылки могут видеть только зарегистрированные и активированные пользователи]

krukovis
22.03.2012, 06:49
DWORD bcount;
Это, на сколько помню, параметр - Количество бит для чтения. и он должен быть равен в твоем случае четырем 4.
А у тебя походу 0.

dwa83
22.03.2012, 06:54
Нет нет, это счётчик количества считанных байт из памяти. Помоему он нафик не нужен даже. Ну если только для проверки всё ли мы считали, что хотели.

Добавлено через 3 минуты
Я вот думаю может с оффсетами чего-то напутал, или может считываю как то не правильно..
Ещё вопрос; вот дана цепочка смещений, доходим до последнего адреса, считываем, и там находится нужное нам значение или адрес нужного нам значения?

Добавлено через 1 час 28 минут
Нашёл наконец таки ошибку) Сыграла свою роль забытость языка и, в частности, способы передачи данных в функцию))

Стоило только преобразовать тип к (void*)BA вместо &BA и всё заработало) Помогли некоторые примеры, написанные на C++, плохо что таковых очень мало тут по сравнению с темами для Delphi(

BritishColonist
22.03.2012, 09:55
dwa83, ознакомьтесь с темой: [Ссылки могут видеть только зарегистрированные и активированные пользователи]
Если интересуют конкретные особенности языка (а в частности: использование WinAPI, DLL, макросов, указателей, классов), то вот ещё две мои темы, правда на другом сайте и по другой игре, но вроде подробно описаны многие вещи:
Creating Your Own DLL Hack ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
Creating and Using Your Own Classes ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
Хотя присутствуют излишества (например, return в конце каждой функции. это необязательно), но это не так важно.

Кстати, работать в любом случае придётся в основном с WinAPI, следовательно, примеры под Delphi тоже будут довольно-таки понятными.

dwa83
23.03.2012, 14:08
Спасибо, ознакомлюсь. С считыванием данных пока разобрался, теперь нужно с инжектом разобраться, пока понятий об этом совсем ноль, ну почти. Почитаю данные темки, если будут вопросы - пишу сюда)
Пока сделал вот такую штуку:)
[Ссылки могут видеть только зарегистрированные и активированные пользователи]

dwa83
26.03.2012, 18:33
Почитал первую темку, понял примерно следующее. Если что-то не так понял, поправьте меня.


В классе игрока вызываем функцию(например таргета)
внутри функции таргета происходит вызов функции InjectAndExecute(иньекция потока и ожидание его завершения, если я правильно понял), в качестве параметров мы передаём указатель на нашу функцию и указатель на параметры для этой функции. То-есть функция уже существует в адресном пространстве НАШЕЙ программы и так же значение параметра лежит гдето тут же у нас в проге..

Далее функция инжекта выделяет в ЧУЖОМ процессе дополнительное место размером с нашу функция и дополнительно место под хранение наших параметров а нам возвращает указатели на эти места(допустим это будет Pf - указатель на место в памяти выделенное в другом процессе под функция и pp - аналогичный указатель на место памяти, где будут храниться параметры для функции. Далее мы берём указатель на функцию в нашей проге и указатель на параметры и, пользуясь ими, копируем нашу функцию с параметрами в выделенную в другом процесе память. Далее мы создаём поток (к многопоточным приложениям я к сожалению отнёсся без интереса когда в своё время насиловал книжку по с++, потому не знаю об этом нифига), как я понимаю этот кусок кода будет выполняться параллельно с основным потоком клиента игры, и по завершению кода этот поток сообщает в то место, откуда мы его создали, а именно в нашу программу, что он завершился. Далее в нашей же программе идёт освобождение памяти в чужом процессе, занимаемой функцией и параметрами.

А что происходит после создания потока с нашей функцией? Она запускается сразу после создания потока?
Так же вопрос: Как сюда Target_THREAD(DWORD* WorldIdentifier) попадает указатель на место? в результате выполнения CreateRemoteThread? А если у нас не один пармаметр передаётся, как их передавать? Допустим у нас есть func_THREAD(DWORD* var1, DWORD* var2) тогда нам по указателю pParams нужно хранить массив с этими двумя параметрами, следующими в порядке их обьявления в параметрах функции?

Допустим всё так, тогда идём дальше. В созданном потоке находится копия кода, вызывающего клиентскую функцию таргета, но уже с нашей модификацией (подставляем свой ID), и далее идёт обычный вызов клиентской функции таргета с как обычно подготовленными регистрами:

pushad // сохраняем все регистры, чтобы не "повредить" стек
mov edx,0x00606A70 // помещаем адрес клиентской функции таргета(Как искать?)
mov edi,Id //тут помещаем ID
mov eax,dword ptr ds:[BA] // Базовый адрес как то используется в функции таргета?
push edi // Тут мы для неё готовим параметр, записав его в стек, если я правильно понял
mov ecx,dword ptr ds:[eax+0x20] // Какоето смещение от базового адреса? что за смещение?
add ecx,0xEC // Как я понял тут идёт по аналогии с *(*BA+0x20)+0xEC не понял что это за цепочка
call edx // собственно вызов функции
popad // возвращаем регистры на место

Дофига осталось непонятных вещей.. А в общем я всё правильно понял?
На ассемблере прогил ооочень давно и совсем на другой платформе(старый добрый z80) но думаю разобраться не составит труда.

BritishColonist
26.03.2012, 21:47
dwa83, вроде всё верно.
В клиенте выделяется память под функцию, в эту область памяти записывается код функции и параметры к ней же, потом происходит вызов функции через CreateRemoteThread, которой в качестве параметров помимо хендла процесса передаются указатели на функцию и параметры к ней (указатели эти получены из памяти клиента при выделении в нём памяти). После вызова CreateRemoteThread, созданный поток сразу же запускается (сам), следовательно, код функции потока сразу же начнёт выполняться. Как только функция потока возвращает управление, поток, кажется, убивается. Я точно не уверен. Примерно в этом состоит основной принцип инжектов.
Код, имитирующий действия клиента все привыкли называть просто инжектами. Инжекты представляют собой код (преимущественно ассемблерный), предназначенный исключительно для внедрения в другой процесс (нет смысла в прямом запуске инжектов из программы). Поэтому некоторые любят получать байткод своих инжектов и записывать байты из него в массив, чтобы не обрамлять инжект в своей программе как обычную функцию.
Передать несколько параметров можно, например, любым структурированным типом (class, struct). Рекомендую использовать именно структуры, так логичнее. То есть нужные параметры можно описать как поля структуры, присвоить им определённые значения, а затем передать в функцию указатель на эту структуру. Причём тут есть два варианта: один набор параметров - одна структура (т.е. в разных инжектах будет использоваться своя структура со своими понятными именами полей, например Player->TargetId), либо (как любят дельфисты портала) одна структура для любых инжектов (но тогда будет тяжелее восприниматься смысл кода, ибо поля будут всегда иметь одинаковые имена, например PParams->Param1).
Как искать адреса, смещения и код для инжекта? Здесь необходим опыт работы в отладчике (например, OllyDbg) и в программе вроде Cheat Engine. CE используется для поиска смещений и кода, работающего с определёнными адресами, а Olly - для анализа этого кода, понимания его логики, выделения значимых его частей. Чтобы понять некоторые основы сего ремесла, нужно читать соответствующие статьи (благо, есть люди, которые пишут их) и уделять много времени копанию в коде клиента.
Вот парочка годных статей от Dinmaite:
поиск базового адреса ([Ссылки могут видеть только зарегистрированные и активированные пользователи]) (об указателях и смещениях);
поиск кода для инжекта ([Ссылки могут видеть только зарегистрированные и активированные пользователи]) (об инъекциях кода).

dwa83
27.03.2012, 15:57
BritishColonist, спасибо огромное за обучалку. Всё получилось, всё работает. С инжектами разобрался, с поиском кода для инжекта тоже, прочитав темы по данным ссылкам. Осталось программу нормально структурировать(пока только есть недоклассы для работы с удалённым процессом, который включает в себя функции считывания данных в различные структуры, списки окружабщих мобов\нпс, параметры персонажа, и одна рабочая функция для инжекта). Осталось найти несколько необходимых кусков кода для инжекта и можно будет стряпать логику поведения самого бота). Спасибо ткнуть увы не могу, сообщений маловато. Вообще не думал что осилю данную тему, но подсказки и обьяснения были на высоте)

Добавлено через 6 часов 2 минуты
Вот вопросик ещё.. искал код использования скилла, нашёл вот это. Что это за вызов такой странный в последней строчке? Ещё интересно вот что, здесь идёт запоминание ESI и EDI, но выше по коду я не вижу вообще откуда в них берутся значения.. как тут поступить? Уже столько раз клиент подвесил) замаялся))

CPU Disasm
Address Command Comments
004B64EF NOP
004B64F0 /. SUB ESP,8
004B64F3 |. PUSH EBP
004B64F4 |. MOV EBP,ECX
004B64F6 |. MOV EAX,DWORD PTR SS:[EBP+8]
004B64F9 |. TEST EAX,EAX
004B64FB |. JNE SHORT 004B6504
004B64FD |. XOR AL,AL
004B64FF |. POP EBP
004B6500 |. ADD ESP,8
004B6503 |. RETN
004B6504 |> MOV ECX,DWORD PTR DS:[0B27A04]
004B650A |. MOV EAX,DWORD PTR DS:[EAX+8]
004B650D |. PUSH ESI
004B650E |. PUSH EDI
004B650F |. MOV EDX,DWORD PTR DS:[ECX+1C]
004B6512 |. PUSH EAX
004B6513 |. MOV ESI,DWORD PTR DS:[EDX+34]
004B6516 |. CALL DWORD PTR DS:[<&ElementSkill.?IsGoblinSkill@ElementSkill@GNET@@SA_ NI@Z>]

BritishColonist
27.03.2012, 22:38
dwa83, вероятно, эти регистры были заполнены до вызова данной функции.
Странный вызов - вызов функции IsGoblinSkill из библиотеки ElementSkill. Честно говоря, я не узнавал, в чём смысл этого названия и что она делает, но это точно не нужно. Если интересно, могу подогнать свой инжект скиллов.

dwa83
28.03.2012, 01:28
Не отказался бы от кода, самому найти не получается. Пытался отсеивать так: искал в СЕ адреса из которых идёт обращение к BA при нажатии на 1 скилл, затем из найденных адресов выбирал те, по которым проходит и при использовании другого скилла, ну и на всякий случай отсеял при использовании скилла и его отмене тут же, чтобы отсеять именно начало использования. При этом нашёл только один нормальный кусок кода, в котором присутствует ID скилла как параметр, но и с ним не получилось, там вроде бы кроме самого ID пара нулей как параметры в стек помещались. В итоге клиент подвисал. А тот код что я выше указал я последним нашёл, когда отсеивал на привращении дру в лису и обратно, причём отсеился единственный кусок кода, к тому же там перед вызовом функции как раз и указывался ID скилла в PUSH EAX, но видимо всё равно не то. Так же пробовал взять из темки готовый адрес, и искал все вызовы call 0xADRES. Таких кусков нашёл штук 5, да и с ними не смог ничего работающего сделать.

BritishColonist
28.03.2012, 02:08
void CPlayer::CastSkillOnTarget(DWORD SkillId) // кастуется строго на цель, но если эффект положительный, а цель - моб/враг, то кастуется на себя
{
DWORD Function = 0x00463630;
__asm {
push -1
push 0
push 0
mov ecx,PlayerStruct
push SkillId
call Function
}
}
void CPlayer::CastSkillOnSelf(DWORD SkillId) // даже если кто-то выделен, скилл пойдёт на себя, но атакующие скиллы не пройдут даже по мобам
{
DWORD wId = *(DWORD*)(PlayerStruct+0x47C);
DWORD PtrToPlayerWId = (DWORD)&wId;
DWORD Function = 0x00697760;
__asm {
push PtrToPlayerWId
push 1
push 0
push SkillId
call Function
}
}

Эти два инжекта я использую из DLL, поэтому параметры передаются не как указатели, а как конкретные числа. И поэтому я напрямую читаю память через указатели во втором инжекте. Впрочем вас скорее заинтересует первый из них, т.к. его проще всего будет правильно внедрить.

dwa83
28.03.2012, 11:52
Спасибо, первый кусок прикрутил. Наконец работает и не виснет ничего). Сделал так вот:

void __stdcall Skill_THREAD(DWORD* ID)
{
DWORD Id = *ID;
DWORD Function = 0x00463630;
__asm
{
push -1
push 0
push 0
mov ecx,DWORD PTR DS:[0x0B27A04]
mov ecx,DWORD PTR DS:[ecx+0x1C]
mov ecx,DWORD PTR DS:[ecx+0x34]
push Id
call Function
}
}

Добавлено через 2 часа 12 минут
Как бы не надоесть в вопросами, но спрошу. Вопрос касается работы в среде с++ builder. У меня вечное недопонимание того, как следует объявлять модули в начале друг друга. Как пример: есть модуль главной программы. В нём я объявляю новый модуль в котором описываю реализацию своего класса. В классе используются некоторые переменные типа структур, эти структуры я тоже описываю в этом же модуле выше описания моего класса. Теперь мне нужно в другом модуле использовать переменные такого же типа как эти структуры. Если их так же описать в другом модуле, получу ошибку повторного описания структуры. Я выношу эти структуры в отдельный модуль, который обьявляю в начале модуля, в котором они были раньше. Так же объявляю этот модуль с описанием структур в другом, где они тоже должны использоваться. В итоге опять же получаю ошибку "Multiple declaration for ...". Если обьявляю этот файл со структурами в модуле главной программы перед обьявлениями двух модулей(в которых используются эти типы), то результат - эти типы не видны в этих модулях. Где можно взять инфу конкретно по этой теме? Даже не знаю каким запросом гуглить.

BritishColonist
28.03.2012, 15:17
dwa83, честно говоря, даже не знаю, что и посоветовать. Я обычно делаю всё одним файлом, чтобы не париться с этими инклудами. C++, знаешь ли.. Тут всё строго : D

dwa83
28.03.2012, 15:28
Наверное так и придётся сделать, но вот код получится слишком громоздким, можно запутаться

Добавлено через 22 часа 0 минут
Кстати вот пока всё что наковырял. По ходу обдумывания поведения бота, поискал некоторые инжекты. Некоторые из них на форуме были на Делфи, я их под себя на с++ переделал. Может кому пригодится, вдруг кто-то на с++ пишет, а переделывать с Делфи не охота). Самолично найти получилось только Target, Fly, CallPet

// Оффсеты
#define BA 0xB27A04 // базовый адрес
#define GA 0xB280C4 // game adress
#define D_GA 0x1C // смщение до GA

// Структура персонажа BA+GA+
#define PERS_STRUCT 0x34 // структура перса

// Параметры персонажа BA+GA+PERSSTRUCT+
#define MY_ACTION_ARRAY 0x1050 // Массив действий
#define MY_PET_ARRAY 0x1068 // Массив петов


// Адреса функций
#define F_TARGET 0x006686E0 //Выделение моба по WID
#define F_SKILL 0x00463630 //Использование скилла по ID
#define F_MOVE1 0x0046E410 //Перемещение персонажа
#define F_MOVE2 0x004728E0 //Перемещение персонажа
#define F_MOVE3 0x0046E880 //Перемещение персонажа
#define F_FLY 0x00668490 //Полёт вкл/выкл
#define F_CALL_PET 0x0046C680 //Вызов пета
#define F_GET_LOOT 0x00469B40 //Подбор лута/шахты

struct COORDS
{
float x;
float y;
float z;
BYTE state;
};

struct LOOT_ITEM
{
DWORD wid;
unsigned short type;
};


//###########################
// Таргет по WID
void __stdcall Target_THREAD(DWORD* WID)
{
DWORD Id = *WID;
__asm
{
MOV EDI, Id
MOV EBX, F_TARGET
MOV EAX,DWORD PTR DS:[BA]
PUSH EDI // ; /Arg1
MOV ECX,DWORD PTR DS:[EAX+0x20] // ; |
ADD ECX,0x0EC // ; |
CALL EBX // ; \elementc.00606A70
}
}

//###########################
// Использование скилла по ID
void __stdcall Skill_THREAD(DWORD* ID)
{
DWORD Id = *ID;
DWORD Function = F_SKILL;
__asm
{
push -1
push 0
push 0
mov ecx,DWORD PTR DS:[BA]
mov ecx,DWORD PTR DS:[ecx+D_GA]
mov ecx,DWORD PTR DS:[ecx+PERS_STRUCT]
push Id
call Function
}
}

//###########################
// Перемещение в координаты
void __stdcall MoveTo_THREAD(COORDS* coord)
{

DWORD Function1 = F_MOVE1;
DWORD Function2 = F_MOVE2;
DWORD Function3 = F_MOVE3;

float x = coord->x;
float y = coord->y;
float z = coord->z;
DWORD flying = coord->state;

__asm
{
mov eax, dword ptr [GA]
mov esi, dword ptr [eax+PERS_STRUCT]
mov ecx, dword ptr [esi+MY_ACTION_ARRAY]
push 1
call Function1
mov edi, eax
lea eax, dword ptr [esp+0x0C]
push eax
push flying
mov ecx, edi
call Function2
mov ecx, dword ptr [esi+MY_ACTION_ARRAY]
push 0
push 1
push edi
push 1
call Function3
mov eax, dword ptr [GA]
mov eax, dword ptr [eax+PERS_STRUCT]
mov eax, dword ptr [eax+MY_ACTION_ARRAY]
mov eax, dword ptr [eax+0x30]
mov ecx, dword ptr [eax+0x4]
mov eax, x
mov dword ptr[ecx+0x20], eax
mov eax, z
mov dword ptr[ecx+0x24], eax
mov eax, y
mov dword ptr[ecx+0x28], eax
}

}

//###########################
// Включение/выключение полёта
void __stdcall Fly_THREAD(WORD* FlyID)
{
DWORD fID = *FlyID;
DWORD Function = F_FLY;
__asm
{
MOV EAX,DWORD PTR DS:[BA]
PUSH 0x1 // ; /Arg4 = 1
PUSH fID // ; |Arg3
MOV ECX,DWORD PTR DS:[EAX+0x20] // ; |
PUSH 0x0C // ; |Arg2 = 0C
PUSH 0x1 // ; |Arg1 = 1
ADD ECX,0x0EC // ; |
CALL Function // ; \elementclient.00668490
}
}

//###########################
// Вызов пета из ячейки
void __stdcall CallPet_THREAD(BYTE* number)
{
DWORD nom = *number;
DWORD Function = F_CALL_PET;
__asm
{
MOV ECX,DWORD PTR DS:[BA]
MOV EAX,nom
MOV EDX,DWORD PTR DS:[ECX+D_GA]
MOV ECX,DWORD PTR DS:[EDX+PERS_STRUCT]
MOV EDX,DWORD PTR DS:[ECX+MY_PET_ARRAY]
PUSH EAX // ; /Arg1 => [ARG.ECX+8]
CALL Function // ; \elementclient.0046C680
}
}

//###########################
// Подбор лута/шахты по WID
void __stdcall GetLoot_THREAD(LOOT_ITEM* itm)
{
DWORD wid = itm->wid;
DWORD typ = itm->type;
DWORD Function = F_GET_LOOT;
__asm
{
mov ecx, dword ptr DS:[GA]
mov ecx, dword ptr DS:[ecx+PERS_STRUCT]
PUSH typ // 0 - лута, 1 - шахт
PUSH wid // wid ресурса
call Function
}
}

dwa83
02.04.2012, 21:01
Не подскажете инжекты покупки продажи? Сколько не искал не получается найти. Так же не хватает ремонта всего снаряжения. Пока что такой бот: Прилетает по заданным координатам, приземляется готовит пета лупит мобов(параметры мобов задаются в окне бота), собирает лут только в близи убитого моба(пропуская тот что не может поднять) до заполнения инвентаря, затем летит продаваться открывает диалог ...... закрывает диалог и возвращается на место фарма.

Вот в пропущеном месте хотелось бы вставить починку, докупку банок и продажу хлама. Может кто-нибудь помочь с этим? В темке с инжектами есть данные функции, но новые адреса к ним я увы не могу найти, поиск приводит к совершенно не похожим на эти функции кускам кода.

Добавлено через 21 минуту
Интересует ещё такой вопрос. В чём суть инжектов пакетов? Я так понимаю это уже готовые для отправки на сервер команды в виде массива байт. И не опаснее ли схватить с ними бан чем с инжектами функций? Ведь при инжектах функций все пакеты клиент готовит сам, а тут отправка напрямую идёт?

BritishColonist
02.04.2012, 21:24
dwa83, я запиливаю продажу пакетами примерно так (из DLL) :


void Packet(DWORD length, char* packet) // отправка пакета
{
DWORD PacketFunc = 0x6737B0;
BYTE* Packet = new BYTE [length];
memcpy(Packet,packet,length);
__asm {
mov ecx, dword ptr [BA]
mov ecx, dword ptr [ecx+0x20]
push length
push Packet
call PacketFunc
}
delete [] Packet;
}


Этот код использую для продажи всех миражей из инвентаря:

char PacketSell[30] = "\x25\x00\x02\x00\x00\x00\x10\x00\x00\x00\x01\x00\x 00\x00\x34\x47\x00\x00\x11\x00\x00\x00\xD0\x07\x00 \x00";
for(SlotId=0; SlotId<SlotCount-1; SlotId++) {
ItemStart = *(DWORD*)(InventoryStart+SlotId*4);
if(ItemStart) { // указатель не равен NULL - предмет есть в ячейке
if (*(DWORD*)(ItemStart+0x08) == 18228) { // ид совпал с нужным
PacketSell[18] = SlotId; // 18-й байт пакета - номер ячейки, из которой хотим продавать
memcpy((void*)(PacketSell+22),(void*)(ItemStart+0x 10),4); // 4 байта, начиная с 22-го, содержат количество предметов, которые хотим продать
Packet(26,PacketSell); // отсылаем пакет
}
}
}


А этот код загружает структуру игрока и некоторые нужные оффсеты:

void CHostPlayer::LoadStruct()
{
while (!*(BYTE*)OnlineFlag) {
Sleep(100);
}

__asm {
mov eax, dword ptr [BA]
mov eax, dword ptr [eax+0x1C]
mov eax, dword ptr [eax+0x34]
mov PlayerStruct, eax
mov eax, dword ptr [eax+0xCAC]
mov ecx, dword ptr [eax+0x10]
mov SlotCount, ecx
mov eax, dword ptr [eax+0x0C]
mov InventoryStart, eax
}
}


Вас скорее заинтересуют не конкретные ID предметов, а их типы (например, если предмет - ДК, его можно продавать). Смещений и самих типов я не знаю, но они должны быть где-то тут есть в соседних темах.

dwa83
02.04.2012, 21:50
Спасибо, попробую пакетами. Надо бы разобраться, судя по всему пакетами сейчас больше функций делают.

Добавлено через 2 часа 16 минут
Что-то я делаю не так, не могу понять что. Я функцию посылки пакета инжектом загоняю в клиент, Как параметр передаю указатель на структуру пакета, которая включает длину и сами байты:
struct PACKET
{
int len;
BYTE Bytes[30];
};

Функция посылки пакета, которая инжектится в процесс такая:
// îòïðàâêà ïàêåòà
void __stdcall SendPacket_THREAD(PACKET* pack)
{

DWORD Function = F_SEND_PACKET;
DWORD len = pack->len;
BYTE Packet[30];
for (int i=0; i<pack->len; i++)
Packet[i]=pack->Bytes[i];


__asm
{
mov ecx, dword ptr [BA]
mov ecx, dword ptr [ecx+0x20]
push len
push DWORD(Packet)
call Function
}

}

Далее функция которая инжектит её выглядит так:

void INJECTOR::Sell(BYTE nomcell, DWORD kol)
{
PACKET pack;
char PacketSell[30] = "\x25\x00\x02\x00\x00\x00\x10\x00\x00\x00\x01\x00\x 00\x00\x34\x47\x00\x00\x11\x00\x00\x00\xD0\x07\x00 \x00";
memcpy(pack.Bytes,&PacketSell,30);
pack.Bytes[18] = nomcell; // 18-é áàéò ïàêåòà - íîìåð ÿ÷åéêè, èç êîòîðîé õîòèì ïðîäàâàòü
memcpy(pack.Bytes+22,&kol,4); // 4 áàéòà, íà÷èíàÿ ñ 22-ãî, ñîäåðæàò êîëè÷åñòâî ïðåäìåòîâ, êîòîðûå õîòèì ïðîäàòü

pack.len=26;

InjectAndExecute(&SendPacket_THREAD, &pack);
}

Сначала процесс подвисал из за того что в функции посылки пакета(которая инжектится в адресное пространство клиента) я пытался создать динамический массив как в примере. Поменял на статический. Компилятор стал ругаться на то что я в ASM коде пытаюсь поместить PUSH Packet. В обычных условиях Имя массива без скобок является указателем на первый элемент массива(т.е. указателем на массив). Но компилятор не хочет почемуто иметь в этом месте указатель типа BYTE*.
Я написал так: push DWORD(Packet). Тоесть мы взяли указатель на BYTE(по сути число, представляющее адрес) и сказали компилятору, что это DWORD число. Компилятор скушал выражение, но при посылке пакета ничего не происходит вообще. Где я напортачил?

Добавлено через 18 часов 36 минут
Всё, разобрался) Утро вечера мудренее, как говорится) Сделал так:
// îòïðàâêà ïàêåòà
void __stdcall SendPacket_THREAD(PACKET* pack)
{

DWORD Function = F_SEND_PACKET;
DWORD len = pack->len;
BYTE Packet[30];
for (int i=0; i<pack->len; i++)
Packet[i]=pack->Bytes[i];
BYTE* Pointer=Packet;

__asm
{
mov ecx, dword ptr [BA]
mov ecx, dword ptr [ecx+0x20]
push len
push Pointer
call Function
}

}

Добавлено через 19 часов 7 минут
BritishColonist, Пакет продажи, мне кажется, не правильный или уже изменён.
Пробовал пакеты медитации и атаки петом, работают.
void INJECTOR::Meditation()
{
PACKET pack;
pack.len=2;
char Packet[2] = "\x2E\x00";
memcpy(pack.Bytes,Packet,pack.len);
InjectAndExecute(&SendPacket_THREAD, &pack);
}

void INJECTOR::PetAttack(DWORD wid)
{
PACKET pack;
pack.len=11;
char Packet[30] = "\x67\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00";
memcpy(pack.Bytes,Packet,pack.len);
memcpy(pack.Bytes+2,&wid,4); // 4 áàéòà, íà÷èíàÿ ñ 2 - WID ìîíñòðà
InjectAndExecute(&SendPacket_THREAD, &pack);
}

а вот пакет продажи ни в какую не хочет. Пробовал с открытым диалогом у нпс и с открытым окном покупки/ремонта. Может ещё надо WID или ИД NPSа указывать в пакете?

Извиняюсь за невнимательность, в пакет же ещё ID предмета нужно записать, а там уже записан ID перьев феникса)

Добавлено через 22 часа 38 минут
Ура, первый самостоятельно написаный бот уже готов, пока я это пишу - фармит, тестируется)

Надо сказать большое спасибо форумчанам за полезные темы, так как научить человека, который не имеет никакого понятия о ботостроение, делать ботов - это прям достижение и гордость :) Тем более за такой срок (Не знаю для кого как, а для меня полторы недельки - это быстро:)).

Спасибо этим людям(могу только + рядом с аватаркой тыкнуть):


BritishColonist За отличный гайд Внедрение и модификация кода ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
И вообще отдельное спасибо за наставления и обьяснения по вопросам.
Dinmaite За темы Код для инжекта в память ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
Поиск базового адреса ([Ссылки могут видеть только зарегистрированные и активированные пользователи])
Поиск инжектов ([Ссылки могут видеть только зарегистрированные и активированные пользователи])

sumikot Очень полезная ТЕМА ([Ссылки могут видеть только зарегистрированные и активированные пользователи]) с оффсетами, страничка практически не закрывалась у меня)

N00bSa1b0t За прогу PW PacketListener ([Ссылки могут видеть только зарегистрированные и активированные пользователи])

Вроде никого не забыл) Щас буду доробатывать бота до ума)

dwa83
05.04.2012, 23:33
Охх, всё, голова уже дымит. Может кто подскажет почему происходят периодические подвисания клиента? Происходит это так: сначала клиент перестаёт отвечать на инжекты, но все данные успешно считываются. Затем, даже если бот уже выключен, клиент реагирует на внутриигровое управление, но спустя время перестаёт отвечать(стандартное виндовое сообщение) либо светофором наградит. Замечено, что происходит это в циклах примерно таких:
void PROCESS::HillPet(float prc)
{
while ((petHPproc(0)<prc)&&(myHP()!=0)) // допустим пет номер 0
{
msg="Хилю пета";
if (skillCoolDown(11)==0) //проверка в откате ли скилл
proc.Skill(skillID(11)); // Инжектится юзанье скилла)
Application->ProcessMessages(); // даём приложению обработать события если хилл затянется
}
}

У кого-нибудь были такие проблемы?

Так же заметил, что из клиента начинают пропадать текстуры и модели, при этом, если свернуть клиент и немного подождать и раскрыть, какое-то время вообще чёрное окошко отображается.
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
а теперь ещё и люди-невидимки появились :D
[Ссылки могут видеть только зарегистрированные и активированные пользователи]

Добавлено через 6 часов 49 минут
BritishColonist, Вы помоему единственный в этой теме отвечаете. Не затруднит исходник глянуть? Или даже потестить. В принципе работает, хоть и не совсем алгоритм тот что задуман, но минут 5 максимум - и всё, не реагирует на инжекты(. Может я чего не правильно делаю?

whoami
06.04.2012, 11:26
Не глядя на код определить практически не возможно =)
скорее всего, что-то важное затираешь, либо у тебя race condition

dwa83
06.04.2012, 14:24
whoami, если не лень посмотреть я ссылку на архив с проектом отправлю в ЛС

Добавлено через 6 минут
Попортить память клиента помоему только этим можно, так как больше нигде в память нет записи:
BYTE INJECTOR::InjectAndExecute(void *Func, void* Params)
{
HANDLE hProcThread;
void* pFunction;
void* pParams;

pFunction = VirtualAllocEx(hProcess,NULL,4096,MEM_COMMIT,PAGE_ READWRITE);
pParams = VirtualAllocEx(hProcess,NULL,256,MEM_COMMIT,PAGE_R EADWRITE);
WriteProcessMemory(hProcess,pFunction,Func,4096,NU LL);
WriteProcessMemory(hProcess,pParams,Params,256,NUL L);

hProcThread = CreateRemoteThread(hProcess,NULL,NULL,(LPTHREAD_ST ART_ROUTINE)pFunction,pParams,NULL,NULL);
if(hProcThread==INVALID_HANDLE_VALUE) // не удалось создать поток
{
VirtualFreeEx(hProcess,pFunction,4096,MEM_RELEASE) ;
VirtualFreeEx(hProcess,pParams,256,MEM_RELEASE);
return 0;
}

WaitForSingleObject(hProcThread,INFINITE); // ожидаем завершения работы потока
CloseHandle(hProcThread); // освобождаем память
VirtualFreeEx(hProcess,pFunction,4096,MEM_RELEASE) ; // стираем из процесса нашу функцию
VirtualFreeEx(hProcess,pParams,256,MEM_RELEASE); // и параметры
return 1; // успешная инъекция и выполнение кода
}

whoami
06.04.2012, 14:30
ok. Но только если посмотреть, погонять сейчас нет возможности.

BritishColonist
06.04.2012, 14:39
dwa83, давайте посмотрим.
Но я уверен на 90%, что даже долго изучая код, я вряд ли смогу определить проблему. Это не так-то просто.
А сейчас я рекомендую проверить, от чего конкретно случается эта штука. Найдите код, который, возможно, грузит процесс игры, уберите его и протестируйте так. Рано или поздно проблемный код будет вычислен.
Инжектор вроде правильно написан, но длительных тестов я с ним никогда не проводил.

edit:
Если зависания и баги появляются лишь спустя длительное время, попробуйте увеличить задержку между обновлением информации о мобах.

dwa83
06.04.2012, 15:12
Отправил ссылку. Вообще вполне возможно что я в инжектах прочу данные стека, так как не использую pushad и popad. Сейчас везде поставил. Ещё раз бота послал, сейчас гляну виснет ли всё ещё. Насчёт задёржки в цикле, попробую если при этом тесте всё равно виснуть продолжит. Вообще раньше я делал так. Одним скопом делал снимки структур мобов, нпс, ресов и тд. а затем работал с ними не считывая пока новые данные не понядобятся. Но так было слишком накладно, потому сейчас я все получения отдельных параметров сделал отдельной функцией, чтобы при желании узнать ХП, не приходилось читать всю структуру.

А что конкретно грузит игру я понял. Это либо инжект хила пета, либо инжект выделения моба, другие скорее всего тоже весят, но я проверял конкретно только эти. Ставил их в цыкл и запускал бота. Я вот только не могу понять почему подвешивает(причём так не сразу). Если из за "порчи стека" - щас тестируется с исправлениями, пока что бегает, не висит.

Добавлено через 6 минут
И почему-то при отдельных действиях, как то "ожидание пока перс начнёт кастовать" проц взлетает до 100%.. Кроме Sleep() можно как то с проца нагрузку снимать? Не охота жертвовать скоростью реакции..

Кстати, на счёт самого бота, к запущеному клиенту он сейчас подключается кнопкой "подключиться к игре", и только потом старт нажимать нужно. А выключается пока что снятием процесса из среды BC ))

Добавлено через 40 минут
Push и Pop не причём. Перестаёт реагировать.. Причём, в игре все действия можно делать в ручную. Например, пишет "выделяю моба" но инжект не идёт. Только если вручную выделить переходит к следующему действию. Ито далее все действия проходят только вручную. То-есть бот пишетт что выполняет действие и ждёт пока закончит его делать, но это действие не проходит, поэтому он ждёт до бесконечности и посылает не действующие инжекты. Щас попробую паузы наставить в те места, где виснет..

whoami
06.04.2012, 15:53
Сразу по оптимизации замечание: на каждый вызов делать VirtualAllocEx/Free, да ещё отдельно для опкода и параметров - не самая лучшая идея. Лучше память аллоцировать один раз, сразу под всё. Потом, большинство операций (скиллы, таргет, призыв петов) делаются с помощью пакетов, лучше по максимуму работать через них. Тут и под новую версию клиента проще будет переделывать, и меньше кода отлаживать. Один раз написал код отправки пакета - дальше пользуешься без инжектов. Ещё какой момент... лучше будет, если вы будете заранее знать размер опкода и параметров. WriteProcessMemory для 4-килобайтовых блоков выполняются сильно медленнее, чем для 511-байтовых, например. А 511 байт - это довольно дофига.

SendPacket_THREAD отгда уж лучше так, без ненужного копирования:

DWORD Function = F_SEND_PACKET;
DWORD len = pack->len;
BYTE* Pointer = pack->Bytes;

dwa83
06.04.2012, 16:13
whoami, почитал я что значит race condition. У меня всё в один поток, я с многопоточностью на вы(не разбирался за ненадобностью). Отсюда у меня вопрос. Всё идёт в основном потоке и не продолжается пока не выполнено требуемое действие. При зацикливании по какой либо причине(ну например сканирование чего-нибудь пока не найдём) шло замораживание окна бота(так как события не обрабатывались). Я это решил путём вставки принудительной обработки событий( Aplication->ProcessMessages() ) в длительном цикле. Отсюда проблема если мы в этом цыкле "схватим" мышкой окно и держим его у нас цикл останавливается на обработке событий(перетаскивания окна). То-есть получается обратная ситуация, раньше алгоритм бота не давал выполняться событиям, теперь же события не дают продоолжиться циклам бота(в том случае если мы копаемся в окне бота в то время, когда он работает). Как этого избежать?

Добавлено через 7 минут
SendPacket_THREAD отгда уж лучше так, без ненужного копирования:
Жмм, точно, но это мелочи, я оптимизацией хотел заняться, когда всё в идеале работать будет. А копирование массива данных пакета я сделал, так как у меня в этой функции почему-то не хочет динамически с памятью работать(вылет клиента). Что-то я не подумал, что можно сразу указатель взять, ведь параметры то уже лежат в адресном пространстве клиента. Возьму на заметку. Кстати именно вопрос о размере инжектируемой функции меня интересовал. 511 байт хватит на самую большую функцию в моём коде? Самая большая по моему инжект перемещения

Добавлено через 11 минут
Что-то типа этого?
pFunction = VirtualAllocEx(hProcess,NULL,512,MEM_COMMIT,PAGE_R EADWRITE);
pParams = pFunction+256; // например пополам
WriteProcessMemory(hProcess,pFunction,Func,256,NUL L);
WriteProcessMemory(hProcess,pParams,Params,256,NUL L);

hProcThread ...
WaitFor...

CloseHandle(hProcThread); // освобождаем память
VirtualFreeEx(hProcess,pFunction,512,MEM_RELEASE); // стираем

Добавлено через 18 минут
Ещё хотел добавить, после проставления задержек бот вроде ведёт себя адекватно. Пока вышеописанные доработки вставлять не буду, останавливать придётся, пусть пока пофармит, может ещё чего всплывёт :) Вывод: клиенту не очень нравится, когда его насилуют чрезвычайно частым считыванием данных, значит всё-таки задержками решать надо. Самое интересное, что Sleep(10) оказалось вполне достаточно.

whoami
06.04.2012, 16:41
Ну вот, например, в старой версии PWFramework (библиотека на C#) неправильно ожидалось завершения инжектированного потока - получались натуральные рейсы =) Кроме того, эта функция может вызываться параллельно самим клиентом, и хрен знает как она на это среагирует =)

По поводу гуя и асинхронности. Самое простое - не делать длительных инжектов. А они там - все не длительные. И вызывать их из обработчика WM_TIMER. То есть, раз в 200 мс (например) опрашиваете интересующие вас параметры, принимаете решение, выполняете действия и перевзводите таймер.

Насчёт опкода и параметров. Обычно параметры занимают довольно мало места (у вас там иногда это вообще DWORD - 4 байта). Размер опкода вам известен. Поэтому, сразу в процессе бота формируете блок нужного размера (размер опкода+размер параметров), сразу в него пишете одним вызовом WriteProcessMemory и то и другое. Просто эта функции - Read/WriteProcessMemory крайне медленные, чем реже её дёргаешь - тем лучше. И почему-то запись блоков в 512 происходит намного медленнее, чем 511 =) Раза так в 4 примерно. Проверял на XP SP3, почему так - не знаю.

dwa83
06.04.2012, 16:57
Насчёт таймера подзадумаю, наверное проще так будет. Хотя я об этом уже думал, но мне показалось, что всё получится слишком запутанным. А так если подумать немного получше, то можно поставить таймер с вызовом 200мс, внутри будет проверяться какая-нибудь переменная, которая имеет значение в зависимости от того какое действие выполнить. И по этому критерию выполнять либо каст, либо бег либо что-то ещё. Наверное что-то типа того:
если переменная Action = пуляем скилл
{
выполнить пуляние скилла
проверить пульнули или нет
если пульнули Action = бежим к следующему
}

если переменная Action = бежим к следующему
{
проверить добежали или нет
если добежали Action = пуляем скилл
}
если переменная Action = бот стой
{
выключаем таймер
}

Итого имеем конечный автомат. Помоему будет неплохо.

BritishColonist
06.04.2012, 17:17
Кстати ;D
Чтобы избавиться от половины всех проблем, связанных с разработкой, можно писать DLL, а не EXE.
А потом DLL внедрять через мой загрузчик или своим "стартером" (в котором, например, можно и конфиг сделать).

Но тут появится другая проблема - управление ботом. Самое простое - ini-файлик, который будет проверяться из DLL.

От каких проблем избавляет техника DLL:
1) инжекты не нужно внедрять (минус половина кода);
2) в инжектах может содержаться любой код (а не только асм и не только локальные переменные);
3) прямая работа с памятью через указатели (никаких Read/WriteProcessMemory);
4) нет необходимости в поиске процесса игры (разве что проверить версию игры можно).
Минусы DLL-Injection:
1) сложно организовать нормальное управление работой бота (можно применять сообщение WM_COPYDATA или "file mapping" для обмена данными между EXE и DLL);
2) сложно лицезреть результаты, если речь идёт о конкретных числах, данных (можно прикрутить функции добавления на экран текста или запись в файл);

Таким образом DLL даёт максимальную "степень внедрения". Полный доступ к процессу без вызова WinAPI-функций, максимальное быстродействие. Настоятельно рекомендую попробовать - авось понравится.

whoami
06.04.2012, 17:53
Я для своего PWFramework сделал функцию самоинжектирования в клиент =) правда, так до ума не довёл.
Смысл такой, что пишешь программу, которая работает с процессом клиента через VirtualAlloc/FreeEx, Read/WriteProcessMemory и CreateRemoteThreadEx. Потом делается совсем небольшая доработка: после выбора клиента процесс "вселяется" в элементклиент, т.е. туда внедряется маленькая dll-загрузчик, которая поднимает CLR, в ней создаёт AppDomain, в котором запускает тот же экзешник. Только сам экзешник может выяснить, запущен он был сам по себе, или в адресном пространстве элемента. И во втором случае он может использовать отдельный экземпляр ProcessMemory, который работает с памятью напрямую (с помощью Marshal).
Не знаю, нормально объяснил, или нет. Но в реальных своих утилитах я это не использовал.

Минус подхода "вся логика в dll" в том, что отлаживаться сложнее и любая серьёзная ошибка валит элементклиент. В случае внешнего приложения, его можно спокойно трассировать, ставить бряки и клиент оно рушит далеко не всегда.

dwa83
06.04.2012, 17:55
Я думаю пока что для меня сложновато это будет) Хотя если в DLL добавить API функции работы с окнами то получится так, что один процесс(по сути поток самой игры и поток DLL) имеет 2 окна, одно - окно клиента, второе - окно бота. Это ведь возможно? Если так, то можно сделать "внедряльщик" DLL, в которой есть создание своего окна и взамиодействие с ним, а затем этот "внедряльщик" просто проверяет, всё ли прошло нормально и завершает работу. Я так понимаю внедрённая DLL не является зависимой от exe, который её внедрял? Если так то можно и визуализацию результатов и управление ботом сюда прикрутить?

whoami
06.04.2012, 18:01
Именно так. DLL можно внедрять и совсем без exe-загрузчика.

BritishColonist
06.04.2012, 19:30
Да, и окно своё создать можно.
Но это относится к сложностям ;D

dwa83
10.04.2012, 19:09
Всё, я в истерике блин. Это закон подлости такой? Отчего клиент глючить начинает? Сидишь, минут 20 наблюдаешь - всё ОК. Только отошёл, всё крахом. На инжекты не реагирует, половина текстур начинают белыми становиться. Задержки ставил такие, что бот тупит по 5 секунд перед сбором очередной кучки. Полный ступор и непонимание.

Добавлено через 9 минут
Как узнать длину функции, если в ней присутствует ещё что-либо кроме ASM?
Например:
void __stdcall SendPacket_THREAD(PACKET* pack)
{
DWORD Function = F_SEND_PACKET;
DWORD len = pack->len;
BYTE Packet[40];
for (int i=0; i<pack->len; i++)
Packet[i]=pack->Bytes[i];
BYTE* Pointer=Packet;

__asm
{
mov ecx, dword ptr [BA]
mov ecx, dword ptr [ecx+0x20]
push len
push Pointer
call Function
}
}

Или можно ли как-нибудь стопануть основной поток клиента на время выполнения потока инжектируемой функции?

whoami
11.04.2012, 10:47
Пишите функции на чистом ассемблере. Это совсем не сложно.
Стопать потоки клиента можно, но так никто не делает (будет тормозить).

BritishColonist
11.04.2012, 14:55
dwa83, могу предложить способ найти свою функцию.
Простой, но эффективный. Заключается в следующем:
1) Ставим самой первой строкой функции команду типа PUSH 0x12345678.
2) Ищем эту такую команду в отладчике.
3) Смотрим вверх до первого NOP. Здесь будет начало функции (не включая NOP).
4) Смотрим вниз до первого NOP. Над NOP должна быть команда RET (или RETN).
Всё, байты, находящиеся между NOP'ами из 3 и 4 пунктов - код функции.
Ах да, потом нашу добавленную команду PUSH стоит удалить, а адрес функции запомнить, чтобы проверять, как она там.

whoami
11.04.2012, 15:39
Я вот ещё что скажу, почему очень не рекомендую свои С-шные функции инжектить. Там компилятор может добавлять всевозможные обвязки (рантайм проверки, обработчики всяких нехороших ситуаций и т.д.), которые в элементе скорее всего работать не будут, а будут делать хрен знает что. В лучшем случае

Вот, например, реальный код (подцепился отладчиком, посмотрел).

0425548F int 3
--- -----------
void <skiped>(void*)
{
04255490 push ebp
04255491 mov ebp,esp
04255493 push 0FFFFFFFFh
04255495 push offset __ehhandler$? <skipped> (42E4901h)
0425549A mov eax,dword ptr fs:[00000000h]
042554A0 push eax
042554A1 sub esp,91Ch
042554A7 mov eax,dword ptr [___security_cookie (433AA4Ch)]
042554AC xor eax,ebp
042554AE mov dword ptr [ebp-64h],eax
042554B1 push eax
042554B2 lea eax,[ebp-0Ch]
042554B5 mov dword ptr fs:[00000000h],eax
<skipped>
0425570E mov ecx,dword ptr [ebp-0Ch]
04255711 mov dword ptr fs:[0],ecx
04255718 pop ecx
04255719 mov ecx,dword ptr [ebp-64h]
0425571C xor ecx,ebp
0425571E call __security_check_cookie (428E9C1h)
04255723 mov esp,ebp
04255725 pop ebp
04255726 ret
--- No source file -------------------------------------------------------------


вот последний call точно обрушит процесс

dwa83
11.04.2012, 18:25
Там компилятор может добавлять всевозможные обвязки (рантайм проверки, обработчики всяких нехороших ситуаций и т.д.)
Хмм, не подумал об этом, надо бы поковыряться в настройках компилятора.

Спасибо, попробую эти предложения, хорошо что хоть есть за что уцепиться чтобы косяк найти)

Добавлено через 1 час 13 минут
BritishColonist, Способом указанным вами мне почему-то не удаётся найти подобную строчку. Причём, если эту строчку вставлять в функцию-член класса, то находит. А функции для инжекта у меня указаны как независимые ни от чего отдельные функции, и в них почему-то не находит..

Пока что на пробу поставил в компиляторе режим Release(и без рунтайм библиотек и без отладочной инфы). Сейчас тестится. Так же пробую тестить не запуская из среды. Может при запуске из среды тоже что-то отладочное в функции прописывается..

Добавлено через 3 часа 30 минут
Пытаюсь записать функцию полностью на ассемблере с целью инжектить её просто в виде кода. Попробовал на примере:
void __stdcall SendPacket_THREAD(PACKET* pack)
{
DWORD len = pack->len;
BYTE* Pointer=pack->Bytes;
__asm
{
mov ecx, dword ptr [BA]
mov ecx, dword ptr [ecx+0x20]
push len
push Pointer
mov eax, F_SEND_PACKET
call eax
}
}

__stdcall означает что наша функция получает параметры через стек. У нас 1 параметр - указатель на структуру(адрес структуры). Значит надо в функции получить его из стека. В структуре 4 первые байта - это параметр len, всё остальное - это данные пакета. Я пробую сделать так:
void __stdcall SendPacket_THREAD(PACKET* pack)
{
__asm
{
mov ecx, dword ptr [BA]
mov ecx, dword ptr [ecx+0x20]
pop eax
mov ebx, dword ptr [eax]
push ebx
add eax, 0x4
push eax
mov eax, F_SEND_PACKET
call eax
ret
}
}
Вылетает. Что не так?

Добавлено через 4 часа 22 минуты
Ничего не понимаю.. Почему компилятор коверкает мою функцию? Оптимизацию вообще отключил..

Ясно пишу
void __stdcall SendPacket_THREAD()
{
//DWORD len = pack->len;
//BYTE* Pointer=pack->Bytes;
__asm
{
mov edx,0 // будет заноситься адрес параметров
mov ecx, dword ptr [BA]
mov ecx, dword ptr [ecx+0x20]
mov eax,[edx]
push eax // len
add edx,4
push edx
mov eax, F_SEND_PACKET
call eax
}
}

В итоге Olly показывает
CPU Disasm
Address Hex dump Command Comments
00401A87 /. 0D 047AB200 OR EAX,00B27A04
00401A8C |. 8B49 20 MOV ECX,DWORD PTR DS:[ECX+20]
00401A8F |. 8B02 MOV EAX,DWORD PTR DS:[EDX]
00401A91 |. 50 PUSH EAX
00401A92 |. 83C2 04 ADD EDX,4
00401A95 |. 52 PUSH EDX
00401A96 |. B8 B0376700 MOV EAX,6737B0
00401A9B |. FFD0 CALL EAX
00401A9D |. 5B POP EBX
00401A9E \. C3 RETN


Добавлено через 7 часов 4 минуты
Пока что бот полчаса или час работает без зависов и появления артефактов. Судя по всему решение проблемы я нашёл. Если через минут 20-30 не повиснет, то теперь точно стало ясно откуда такие странные глюки клиента. Судя по всему дело не в отсутствии задержек и частых вызовах инжектов(хотя это и влияло на длительность работы без ошибок), и даже не в конфликте потоков, обращающихся одновременно к одним данным. Обвязка компилятором кода различной белибердой тоже не причина(проверял дизасм - там всё лаконично, хоть и не так как я хотел видеть). Оказывается просто напросто со временем происходила порча стека(не переполнение а наоборот) и он начинал показывать на нужные клиенту данные портя их. Это произошло вследствии использования протокола функций __stdcall без знания того, что это вообще такое. Тоесть все инжектируемые функции обязывались "почистить" за собой стек, что они и делали. Но вызывающий код использовал другой протокол обмена параметрами в функциях(в настройках проекта совсем не stdcall выставлен был). В итоге вызывающий код после выполнения инжекта ещё раз чистил уже чищеный стек. Убрал все stdkall перед фуекциями, давая тем самым компилятору использовать для вызываемой и вызывабщей функции один протокол обмена параметрами и всё:)

Добавлено через 20 часов 45 минут
Похоже я ошибся( глюки всё равно вылазят спустя какое то время, но не пропали( всё, вообще не понимаю что к чему..

dwa83
12.04.2012, 19:38
Пытаюсь, как было замечено в недостатках, оптимизировать.

Переделал инжектор чтобы он инжектил просто массив кода. Всё ли тут верно и нужно ли что-то поменять\добавить\убавить?

BYTE INJECTOR::InjectAndExecute2(void *Func, int len)
{
HANDLE hProcThread;
void* pFunction;

pFunction = VirtualAllocEx(hProcess,NULL,len,MEM_COMMIT,PAGE_R EADWRITE);
WriteProcessMemory(hProcess,pFunction,Func,len,NUL L);
hProcThread = CreateRemoteThread(hProcess,NULL,NULL,(LPTHREAD_ST ART_ROUTINE)pFunction,NULL,NULL,NULL);
if(hProcThread==INVALID_HANDLE_VALUE) // íå óäàëîñü ñîçäàòü ïîòîê
{
VirtualFreeEx(hProcess,pFunction,len,MEM_RELEASE);
return 0;
}

WaitForSingleObject(hProcThread,INFINITE); // îæèäàåì çàâåðøåíèÿ ðàáîòû ïîòîêà
CloseHandle(hProcThread); // îñâîáîæäàåì ïàìÿòü
VirtualFreeEx(hProcess,pFunction,len,MEM_RELEASE); // ñòèðàåì èç ïðîöåññà íàøó ôóíêöèþ
return 1; // óñïåøíàÿ èíúåêöèÿ è âûïîëíåíèå êîäà
}

и инжектируемая функция
void INJECTOR::targetmob(DWORD wid)
{
char fdata[28]="\xBF\x00\x00\x00\x00\xBB\x00\x00\x00\x00\xA1\x00\x 00\x00\x00\x57\x8B\x48\x20\x81\xC1\xEC\x00\x00\x00 \xFF\xD3\xC3";
DWORD func=F_TARGET;
DWORD ba=BA;
memcpy(fdata+1,&wid,4);
memcpy(fdata+6,&func,4);
memcpy(fdata+11,&ba,4);
InjectAndExecute2(&fdata,28);
}

BritishColonist
12.04.2012, 21:49
stdcall - соглашение о передаче параметров. Вроде бы stdcall указывает, что порядок параметров обратный. Мы об этом вообще не задумываемся, т.к. нам эти параметры самостоятельно из стека вынимать не надо, к ним мы обращаемся просто по имени соответствующей переменной. Так что никаких pop в асме быть не должно, если это не предусмотрено инжектом.
Вряд ли дело в компиляторе, т.к. лично я никаких специальных настроек не делаю (даже если меняю конфигурацию (Debug или Release), это не вызывает никаких изменений результирующего кода ассемблера).

И в инжектах можно использовать любой код C++, можно объявлять новые переменные и т.д.
Главное - не ссылаться на то, что находится за пределами видимости данной функции.

dwa83
13.04.2012, 01:09
Вроде бы stdcall указывает, что порядок параметров обратный
не только это, но ещё и говорит о том, что параметры передаются через стек, а так же о том, что вызывающий код не будет чистить стек, а вызываемая функция сама должна это сделать. У меня в настройках проекта стояло cdecl - Аргументы передаются через стек, но очистку стека производит вызывающая программа. А, для примера, в соглашении fastcall вообще стек не нужен, так как через регистры передача параметров. Так что не только в порядке параметров тут дело :)
Но это не важно. Я вот проблему свою не знаю как решить( Сейчас все инжекты переделал, инжекчу напрямик массив кода, как посоветовали. Перед инжектом прямо в код параметры пишу, чтобы функции вообще без параметров были. Этим исключил всякие обвязки кода и тд. Проблема не исчезла. Я грешу на неправильность написания мной некорорых самонайденных инжектов, может в асме что-то пропустил перед вызовом. Заменил их всех на пакеты. Посмотрю что будет теперь..

Всё равно виснет( полчаса работы и потом виснет.. короче мне не понять..

whoami
13.04.2012, 12:03
а ты pusha/popa делаешь?

dwa83
13.04.2012, 13:55
Да, но сначала не ставил, потом, когда глюки начали проявляться, везде расставил, не помогло. Хотя сейчас, когда инжектится готовый массив кода, он их в себя не включает, сейчас расставлю снова попробую.

whoami
13.04.2012, 14:05
Вообще, возьми код из PWFramework, вместе с инжектами, там всё 100% работающее. Я думаю, перевести с c# на C++ будет не сложно.

dwa83
13.04.2012, 14:34
Сейчас расставил пока что во всех функциях(которые не пакетами) 0x60 (PUSHAD) и в конце перед RETN 0x61(POPAD), снова запустил, проверяю. Если опять выскочит неполадка, полезу фреймворк ковырять.

На всякий случай вод весь код инжектора, может посмотрите, кому не лень. Вдруг где ошибка.
#include <vcl.h>
#include "offsets.h"

struct PACKET
{
int len;
BYTE Bytes[40];
};


struct WIN_ACTION
{
DWORD WinOffset;
DWORD ControlOffset;
};

struct COORDS
{
float x;
float y;
float z;
};

struct MOVE_PARAM
{
COORDS coord;
BYTE state;
};

struct LOOT_ITEM
{
DWORD wid;
WORD type;
};

struct CELL_ITEM
{
DWORD id;
DWORD CellNum;
};

struct INJECTOR
{
HWND hwndPW; // Õýíäë îêíà Perfect World
DWORD pid; // Èäåíòèôèêàòîð Ïðîöåññà
HANDLE hProcess; // Õýíäë ïðîöåññà

void TargetMob(DWORD wid);
void Skill(DWORD id);
void Move(float x, float y, float z, int walkmode);
void Fly(DWORD FlyID);
void CallPet(int nom);
void GetLoot(DWORD wid, BYTE type);
void OpenDialog(DWORD wid);
void UseItem(DWORD id, int cell);
void RepairAll();
void WinAction(DWORD WinOffset, DWORD ControlOffset);
void Sell(BYTE nomcell, DWORD kol, DWORD ID);
void Buy(BYTE NPCcell, DWORD kol, DWORD ID);
void Meditation();
void PetAttack(DWORD wid);
void PetGuard();
void PetSkill(DWORD mobwid, DWORD skillid);
void PetDefSkill(DWORD skillid);
void Repair();
void Res();
void Print();


BYTE InjectAndExecute(void *Func, void* Params);
BYTE InjectAndExecute2(void *Func, int len);
BYTE SendPacket(PACKET* pack);



void StrToChar(char* str, char* mas);
};

//__________________________________________________ ____________________________
// Èíúåêòîð ôóíêöèé
/*BYTE INJECTOR::InjectAndExecute(void *Func, void* Params)
{
HANDLE hProcThread;
DWORD* pFunction;
DWORD* pParams;

pFunction = (DWORD*)VirtualAllocEx(hProcess,NULL,511,MEM_COMMI T,PAGE_READWRITE);
pParams = pFunction + 256;
WriteProcessMemory(hProcess,pFunction,Func,256,NUL L);
WriteProcessMemory(hProcess,pParams,Params,255,NUL L);

hProcThread = CreateRemoteThread(hProcess,NULL,NULL,(LPTHREAD_ST ART_ROUTINE)pFunction,pParams,NULL,NULL);
if(hProcThread==INVALID_HANDLE_VALUE) // íå óäàëîñü ñîçäàòü ïîòîê
{
VirtualFreeEx(hProcess,pFunction,511,MEM_RELEASE);
return 0;
}

WaitForSingleObject(hProcThread,INFINITE); // îæèäàåì çàâåðøåíèÿ ðàáîòû ïîòîêà
CloseHandle(hProcThread); // îñâîáîæäàåì ïàìÿòü
VirtualFreeEx(hProcess,pFunction,511,MEM_RELEASE); // ñòèðàåì èç ïðîöåññà íàøó ôóíêöèþ
return 1; // óñïåøíàÿ èíúåêöèÿ è âûïîëíåíèå êîäà
} */

//__________________________________________________ ____________________________
// Èíúåêòîð ôóíêöèé
BYTE INJECTOR::InjectAndExecute2(void *Func, int len)
{
HANDLE hProcThread;
void* pFunction;

pFunction = VirtualAllocEx(hProcess,NULL,len,MEM_COMMIT,PAGE_R EADWRITE);
WriteProcessMemory(hProcess,pFunction,Func,len,NUL L);
hProcThread = CreateRemoteThread(hProcess,NULL,NULL,(LPTHREAD_ST ART_ROUTINE)pFunction,NULL,NULL,NULL);
if(hProcThread==INVALID_HANDLE_VALUE) // íå óäàëîñü ñîçäàòü ïîòîê
{
VirtualFreeEx(hProcess,pFunction,len,MEM_RELEASE);
return 0;
}

WaitForSingleObject(hProcThread,INFINITE); // îæèäàåì çàâåðøåíèÿ ðàáîòû ïîòîêà
CloseHandle(hProcThread); // îñâîáîæäàåì ïàìÿòü
VirtualFreeEx(hProcess,pFunction,len,MEM_RELEASE); // ñòèðàåì èç ïðîöåññà íàøó ôóíêöèþ
return 1; // óñïåøíàÿ èíúåêöèÿ è âûïîëíåíèå êîäà
}


//__________________________________________________ ____________________________
// îòïðàâêà ïàêåòà
/*void SendPacket_THREAD(PACKET* pack)
{
DWORD len = pack->len;
BYTE* Pointer=pack->Bytes;
__asm
{
mov ecx, dword ptr [BA]
mov ecx, dword ptr [ecx+0x20]
push len
push Pointer
mov eax, F_SEND_PACKET
call eax
retn
}
} */

BYTE INJECTOR::SendPacket(PACKET* pack)
{
HANDLE hProcThread;
void* pFunction;
void* pParam;

char fdata[29]="\x60\x8B\x0D\x00\x00\x00\x00\x8B\x49\x20\x68\x11\x 11\x11\x11\x68\x22\x22\x22\x22\xB8\x33\x33\x33\x33 \xFF\xD0\x61\xC3";
int lenfunc=29;
DWORD func=F_SEND_PACKET;
DWORD ba=BA;
DWORD len=pack->len;

pParam = VirtualAllocEx(hProcess,NULL,len,MEM_COMMIT,PAGE_R EADWRITE);
WriteProcessMemory(hProcess,pParam,pack->Bytes,len,NULL);

DWORD addr=DWORD(pParam);
memcpy(fdata+3,&ba,4);
memcpy(fdata+11,&len,4);
memcpy(fdata+16,&addr,4);
memcpy(fdata+21,&func,4);

pFunction = VirtualAllocEx(hProcess,NULL,lenfunc,MEM_COMMIT,PA GE_READWRITE);
WriteProcessMemory(hProcess,pFunction,fdata,lenfun c,NULL);


hProcThread = CreateRemoteThread(hProcess,NULL,NULL,(LPTHREAD_ST ART_ROUTINE)pFunction,NULL,NULL,NULL);
if(hProcThread==INVALID_HANDLE_VALUE) // íå óäàëîñü ñîçäàòü ïîòîê
{
VirtualFreeEx(hProcess,pFunction,lenfunc,MEM_RELEA SE);
VirtualFreeEx(hProcess,pParam,len,MEM_RELEASE);
return 0;
}

WaitForSingleObject(hProcThread,INFINITE); // îæèäàåì çàâåðøåíèÿ ðàáîòû ïîòîêà
CloseHandle(hProcThread); // îñâîáîæäàåì ïàìÿòü
VirtualFreeEx(hProcess,pFunction,lenfunc,MEM_RELEA SE); // ñòèðàåì èç ïðîöåññà íàøó ôóíêöèþ
VirtualFreeEx(hProcess,pParam,len,MEM_RELEASE);
return 1; // óñïåøíàÿ èíúåêöèÿ è âûïîëíåíèå êîäà
}


//__________________________________________________ ____________________________
// Òàðãåò ìîáà ïî WID
/*void INJECTOR::TargetMob(DWORD wid)
{
char fdata[28]="\xBF\x00\x00\x00\x00\xBB\x00\x00\x00\x00\xA1\x00\x 00\x00\x00\x57\x8B\x48\x20\x81\xC1\xEC\x00\x00\x00 \xFF\xD3\xC3";
DWORD func=F_TARGET;
DWORD ba=BA;
memcpy(fdata+1,&wid,4);
memcpy(fdata+6,&func,4);
memcpy(fdata+11,&ba,4);
InjectAndExecute2(&fdata,28);
} */

void INJECTOR::TargetMob(DWORD wid)
{
PACKET pack;
pack.len=6;
char Packet[6] = "\x02\x00\x01\x00\x00\x00";
memcpy(pack.Bytes,Packet,pack.len);
memcpy(pack.Bytes+2,&wid,4);
SendPacket(&pack);
}


//__________________________________________________ ____________________________
// Èñïîëüçîâàíèå ñêèëëà ïî ID
void INJECTOR::Skill(DWORD id)
{
char fdata[39]="\x60\x6A\xFF\x6A\x00\x6A\x00\x68\x00\x00\x00\x00\x 8B\x0D\x00\x00\x00\x00\x8B\x89\x00\x00\x00\x00\x8B \x89\x00\x00\x00\x00\xB8\x00\x00\x00\x00\xFF\xD0\x 61\xC3";

DWORD func=F_SKILL;
DWORD ba=BA;
DWORD dga=D_GA;
DWORD ps=PERS_STRUCT;

memcpy(fdata+8,&id,4);
memcpy(fdata+14,&ba,4);
memcpy(fdata+20,&dga,4);
memcpy(fdata+26,&ps,4);
memcpy(fdata+31,&func,4);
InjectAndExecute2(&fdata,39);
}


//__________________________________________________ ____________________________
// Ïåðåìåùåíèå â êîîðäèíàòû
void INJECTOR::Move(float x, float y, float z, int walkmode)
{
char fdata[117]="\x60\xA1\x00\x00\x00\x00\x8B\xB0\x11\x11\x11\x11\x 8B\x8E\x22\x22\x22\x22\x6A\x01\xBB\x33\x33\x33\x33 \xFF\xD3\x89\xC7\x8D\x44\xE4\x0C\x50\x68\x44\x44\x 44\x44\x89\xF9\xBB\x55\x55\x55\x55\xFF\xD3\x8B\x8E \x66\x66\x66\x66\x6A\x00\x6A\x01\x57\x6A\x01\xBB\x 77\x77\x77\x77\xFF\xD3\xA1\x88\x88\x88\x88\x8B\x80 \x99\x99\x99\x99\x8B\x80\xAA\xAA\xAA\xAA\x8B\x40\x 30\x8B\x48\x04\xB8\xBB\xBB\xBB\xBB\x89\x41\x20\xB8 \xCC\xCC\xCC\xCC\x89\x41\x24\xB8\xDD\xDD\xDD\xDD\x 89\x41\x28\x61\xC3";
DWORD func1=F_MOVE1;
DWORD func2=F_MOVE2;
DWORD func3=F_MOVE3;
DWORD ga=GA;
DWORD ps=PERS_STRUCT;
DWORD maa=MY_ACTION_ARRAY;
DWORD wmode=1; if (walkmode==0) wmode=0;


memcpy(fdata+2,&ga,4);
memcpy(fdata+8,&ps,4);
memcpy(fdata+14,&maa,4);
memcpy(fdata+21,&func1,4);
memcpy(fdata+35,&wmode,4);
memcpy(fdata+42,&func2,4);
memcpy(fdata+50,&maa,4);
memcpy(fdata+62,&func3,4);
memcpy(fdata+69,&ga,4);
memcpy(fdata+75,&ps,4);
memcpy(fdata+81,&maa,4);
memcpy(fdata+92,&x,4);
memcpy(fdata+100,&z,4);
memcpy(fdata+108,&y,4);

InjectAndExecute2(&fdata,117);
}

//__________________________________________________ ____________________________
// Âêëþ÷åíèå/âûêëþ÷åíèå ïîë¸òà
/*void INJECTOR::Fly(DWORD FlyID)
{
char fdata[33]="\x6A\x01\x68\x00\x00\x00\x00\xA1\x00\x00\x00\x00\x 8B\x48\x20\x6A\x0C\x6A\x01\x81\xC1\xEC\x00\x00\x00 \xBB\x00\x00\x00\x00\xFF\xD3\xC3";
DWORD func=F_FLY;
DWORD ba=BA;
memcpy(fdata+3,&FlyID,4);
memcpy(fdata+8,&ba,4);
memcpy(fdata+26,&func,4);
InjectAndExecute2(&fdata,33);
} */

void INJECTOR::Fly(DWORD FlyID)
{
PACKET pack;
pack.len=10;
char Packet[10] = "\x28\x00\x01\x01\x0C\x00\xF7\x31\x00\x00";
memcpy(pack.Bytes,Packet,pack.len);
memcpy(pack.Bytes+6,&FlyID,4);
SendPacket(&pack);
}

//__________________________________________________ ____________________________
// Âûçîâ ïåòà èç ÿ÷åéêè
/*void INJECTOR::CallPet(int nom)
{
char fdata[38]="\x8B\x0D\x00\x00\x00\x00\xB8\x00\x00\x00\x00\x8B\x 91\x11\x11\x11\x11\x8B\x8A\x22\x22\x22\x22\x8B\x91 \x33\x33\x33\x33\x50\xBB\x44\x44\x44\x44\xFF\xD3\x C3";
DWORD func=F_CALL_PET;
DWORD ba=BA;
DWORD dga=D_GA;
DWORD prs=PERS_STRUCT;
DWORD pts=PET_STRUCT;
memcpy(fdata+2,&ba,4);
memcpy(fdata+7,&nom,4);
memcpy(fdata+13,&dga,4);
memcpy(fdata+19,&prs,4);
memcpy(fdata+25,&pts,4);
memcpy(fdata+31,&func,4);
InjectAndExecute2(&fdata,38);
} */

void INJECTOR::CallPet(int nom)
{
PACKET pack;
pack.len=6;
char PacketSell[6] = "\x64\x00\x01\x00\x00\x00";
memcpy(pack.Bytes,PacketSell,pack.len);
pack.Bytes[2]=nom;
SendPacket(&pack);
}

//__________________________________________________ ____________________________
// Ïîäáîð ëóòà/øàõòû ïî WID
void INJECTOR::GetLoot(DWORD wid, BYTE type)
{
char fdata[29]="\x60\x8B\x0D\x00\x00\x00\x00\x8B\x89\x11\x11\x11\x 11\x6A\x22\x68\x33\x33\x33\x33\xBB\x44\x44\x44\x44 \xFF\xD3\x61\xC3";
DWORD func=F_GET_LOOT;
DWORD ga=GA;
DWORD prs=PERS_STRUCT;
BYTE typ=0; if (type==2) typ=1;
memcpy(fdata+3,&ga,4);
memcpy(fdata+9,&prs,4);
fdata[14]=typ;
memcpy(fdata+16,&wid,4);
memcpy(fdata+21,&func,4);
InjectAndExecute2(&fdata,29);
}

/*void INJECTOR::GetLoot(DWORD wid, DWORD id)
{
PACKET pack;
pack.len=10;
char PacketSell[10] = "\x06\x00\xDD\x51\x12\xC0\x0B\x03\x00\x00";
memcpy(pack.Bytes,PacketSell,pack.len);
memcpy(pack.Bytes+2,&wid,4);
memcpy(pack.Bytes+6,&id,4);
SendPacket(&pack);
} */

//__________________________________________________ ____________________________
// Îòêðûòèå äèàëîãà ñ ÍÏÑ ïî WID
void INJECTOR::OpenDialog(DWORD wid)
{
char fdata[30]="\x60\x8B\x15\x00\x00\x00\x00\x8B\x4A\x20\x68\x11\x 11\x11\x11\x81\xC1\xEC\x00\x00\x00\xBB\x22\x22\x22 \x22\xFF\xD3\x61\xC3";
DWORD func=F_DIALOG;
DWORD ba=BA;
memcpy(fdata+3,&ba,4);
memcpy(fdata+11,&wid,4);
memcpy(fdata+22,&func,4);
InjectAndExecute2(&fdata,30);
}

//__________________________________________________ ____________________________
// Èñïîëüçîâàíèå ïðåäìåòà
/*void INJECTOR::UseItem(DWORD id, int cell)
{
char fdata[37]="\x6A\x01\x68\x00\x00\x00\x00\x68\x11\x11\x11\x11\x 6A\x00\x8B\x0D\x22\x22\x22\x22\x8B\x49\x20\x81\xC1 \xEC\x00\x00\x00\xBB\x33\x33\x33\x33\xFF\xD3\xC3";
DWORD func=F_USE_ITEM;
DWORD ba=BA;
memcpy(fdata+3,&id,4);
memcpy(fdata+8,&cell,4);
memcpy(fdata+16,&ba,4);
memcpy(fdata+30,&func,4);
InjectAndExecute2(&fdata,37);
} */

void INJECTOR::UseItem(DWORD id, int cell)
{
PACKET pack;
pack.len=10;
char PacketSell[10] = "\x28\x00\x00\x01\x0D\x00\xAD\x21\x00\x00";
memcpy(pack.Bytes,PacketSell,pack.len);
pack.Bytes[4] = cell; // íîìåð ÿ÷åéêè
memcpy(pack.Bytes+6,&id,4);
SendPacket(&pack);
}


//__________________________________________________ ____________________________
// Çàêðûòèå îêíà
void INJECTOR::WinAction(DWORD WinOffset, DWORD ControlOffset)
{
char fdata[38]="\x61\xA1\x00\x00\x00\x00\x8B\x40\x1C\x8B\x40\x18\x 8B\x40\x08\xBB\x11\x11\x11\x11\x8B\x04\x03\x50\x68 \x22\x22\x22\x22\xBA\x33\x33\x33\x33\xFF\xD2\x60\x C3";
DWORD func=F_WIN_ACTION;
DWORD ba=BA;
memcpy(fdata+2,&ba,4);
memcpy(fdata+16,&WinOffset,4);
memcpy(fdata+25,&ControlOffset,4);
memcpy(fdata+30,&func,4);
InjectAndExecute2(&fdata,38);
}

void INJECTOR::Sell(BYTE nomcell, DWORD kol, DWORD ID)
{
PACKET pack;
pack.len=26;
char PacketSell[26] = "\x25\x00\x02\x00\x00\x00\x10\x00\x00\x00\x01\x00\x 00\x00\xFF\xFF\x00\x00\xFF\x00\x00\x00\xD0\x07\x00 \x00";
memcpy(pack.Bytes,PacketSell,pack.len);
pack.Bytes[18] = nomcell; // 18 - íîìåð ÿ÷åéêè
memcpy(pack.Bytes+22,&kol,4); // 22-25 - êîëè÷åñòâî
memcpy(pack.Bytes+14,&ID,4); // 14-17 - ID ïðåäìåòà
SendPacket(&pack);
}

void INJECTOR::Buy(BYTE NPCcell, DWORD kol, DWORD ID)
{
PACKET pack;
pack.len=38;
char PacketSell[38] = "\x25\x00\x01\x00\x00\x00\x1C\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00 \x00\xAA\xAA\xAA\xAA\xBB\x00\x00\x00\xCC\xCC\xCC\x CC";
memcpy(pack.Bytes,PacketSell,pack.len);
pack.Bytes[30] = NPCcell; // 30 - íîìåð ÿ÷åéêè
memcpy(pack.Bytes+34,&kol,4); // 34-27 - êîëè÷åñòâî
memcpy(pack.Bytes+26,&ID,4); // 26-29 - ID ïðåäìåòà
SendPacket(&pack);
}

void INJECTOR::Meditation()
{
PACKET pack;
pack.len=2;
char Packet[2] = PACK_MEDITATION;
memcpy(pack.Bytes,Packet,pack.len);
SendPacket(&pack);
}

void INJECTOR::PetAttack(DWORD wid)
{
PACKET pack;
pack.len=11;
char Packet[11] = "\x67\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00";
memcpy(pack.Bytes,Packet,pack.len);
memcpy(pack.Bytes+2,&wid,4); // 4 áàéòà, íà÷èíàÿ ñ 2 - WID ìîíñòðà
SendPacket(&pack);
}

void INJECTOR::Repair()
{
PACKET pack;
pack.len=16;
char Packet[16] = "\x25\x00\x03\x00\x00\x00\x06\x00\x00\x00\xFF\xFF\x FF\xFF\x00\x00";
memcpy(pack.Bytes,Packet,pack.len);
SendPacket(&pack);
}

void INJECTOR::Res()
{
PACKET pack;
pack.len=2;
char Packet[2] = "\x04\x00";
memcpy(pack.Bytes,Packet,pack.len);
SendPacket(&pack);
}

void INJECTOR::PetGuard()
{
PACKET pack;
pack.len=14;
char Packet[14] = "\x67\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x 00\x00";
memcpy(pack.Bytes,Packet,pack.len);
SendPacket(&pack);
}

void INJECTOR::PetSkill(DWORD mobwid, DWORD skillid)
{
PACKET pack;
pack.len=15;
char Packet[15] = "\x67\x00\x38\x15\x10\x80\x04\x00\x00\x00\xED\x02\x 00\x00\x00";
memcpy(pack.Bytes,Packet,pack.len);
memcpy(pack.Bytes+2,&mobwid,4);
memcpy(pack.Bytes+10,&skillid,4);
SendPacket(&pack);
}

void INJECTOR::PetDefSkill(DWORD skillid)
{
PACKET pack;
pack.len=14;
char Packet[14] = "\x67\x00\x00\x00\x00\x00\x05\x00\x00\x00\xF1\x02\x 00\x00";
memcpy(pack.Bytes,Packet,pack.len);
memcpy(pack.Bytes+10,&skillid,4);
SendPacket(&pack);
}


Кстати, половина функций закомментировано /* */, но форум это не показывает

Добавлено через 7 часов 20 минут
Посмотрел я фреймворк для c#, всё совершенно равнозначно, кроме одного. Автор перед каждым инжектом открывает доступ к процессу, а после закрывает
// Получаем дескриптор процесса, выбранного клиента PW и открываем память для чтения / записи
if (window != null) MemoryManager.OpenProcess(window.ProcessId);

// Отправляем пакет на медитацию
SendPacket(MemoryManager.OpenProcessHandle, new byte[] { 0x2e, 0x00 });

// Закрываем дескриптор процесса
MemoryManager.CloseProcess();

У мнея же процесс открывается один раз вначале подключения бота к клиенту и закрывается по окончанию работы. Может в этом моя ошибка, и во время работы бота у процесса клиента меняется пид или хэндл или ещё что? Сделаю как у автора PWFramework. Если не поможет, наверное единственным выходом будет признать полную свою криворукость, сойти с ума, забиться в угол, изредка подрыгивая ногой)))

Добавлено через 19 часов 58 минут
Переделал всё нафиг, полностью) Теперь в главном модуле достаточно обьявить структуру типа BOT bot и с помощью неё делать всё что угодно, например:
bot.client.init(); // поиск-нашего процесса и инициализация некоторых параметров
int HP = bot.client.get.myHP(); // так слизываются все значения с клиента
bot.client.inject.TargetMob();

так же переделал нафиг всю логику. теперь можно просто включить режим
bot.st.action=A_SWITCHER
и этот режим в зависимости от полученых с клиента данных переключает различные режимы в зависимости от приоритета.

например, внутри режима свитчера идёт сохранение в специально созданный для этого стек значения текущего режима и может быть включен другой типа
bot.st.push(); // сохранили режим в стек, чтобы по окончанию следующего вернуться в этот
bot.st.action=A_GOTO_FARM; // включает режим перемещения к точке фарма(из него идёт переключение на другие режимы в зависимости от ситуации, например режим всплытия, взлёта, и возврат к предыдущему режиму)

по окончании действия можно вернуться в предыдущий режим, даже не зная какой он был, просто указав
bot.st.pop();
В итоге бот ведёт себя нелинейно, и выбирает действия по мере требования а не по какому либо порядку.

PS: А клиен как переставал реагировать, так и перестаёт:) Так и выходит, что получилось то всё неплохо, но бесполезно:(

dwa83
15.04.2012, 06:10
на каждый вызов делать VirtualAllocEx
Дурная моя голова и слепые глаза/facepalm
Моя невнимательность меня подводит. Проблема заключалась в том, что я при каждом инжекте так и делал, но после определённого промежутка времени (20-30мин) VirtualAllocEx начинала отказы в выделении памяти. А, ввиду того, что проверок на ошибки в коде нет вообще, инжект писался хз куда, в NULL:notme:. Плюс к этому дальше шло ещё и освобождение памяти по NULL, итого вообще полный ппц. Теперь сделал выделение памяти один раз, при инициализации. Пока что работает вроде бы, но с моей внимательностью может ещё что всплывёт.

whoami
16.04.2012, 14:59
dwa83, может быть память у тебя не освобождалась?

dwa83
16.04.2012, 21:13
Возможно, но проверять уже не охота) сейчас всё отлично работает) Кстати почитал недавно темку "ресокоп", у него точно такая же проблема была.

dwa83
19.04.2012, 10:08
Пытаюсь сделать вход в игру после разрыва соединения. В теме с оффсетами указан офсет онлайна
OnlineFlag = 00B27CE0 /В игре - 1, не в игре - 0/
Но вот проблема в том, что он показывает онлайн при появлении окошка "Соединение с сервером было разорвано", а оффлайн показывает только вне игры, например на странице выбора персонажа. Как отловить появление окна "соединение с сервером разорвано"?

Добавлено через 1 час 5 минут
Флаг коннекта/дисконнекта нашёл - 0xB36C38