Re: [Diablo III] Бот для игры, взлом, слезы о банах евро аккаунтов.
с радостью поддержу ваши начинания , сам рылся все основные показатели таргентинга и перемещений в памяти видны .НО судя по анализу (и небольшому опыту) все изменения значений в памяти не игровым клиентом засекаются и со временем караются.Считаю наилучшим вариантом все таки обезапаситься и сделать (что будет трудно) все на графическом анализе
global$sModule="Diablo III.exe"global$ProcessID=WinGetProcess(" Diablo(не помню точно название окна на бетке другое было ;D) ")_GetPrivilege_SEDEBUG()global$DllInformation=_MemoryOpen($ProcessID)global$baseADDR=_MemoryModuleGetBaseAddress($ProcessID,$sModule);оффсеты ниже думаю у тя есть :-\ а то это с RQFuncDataOp($Param1,$Param2="no param",$Param3="no param",$ToDo="Read",$Data=0)Global$Base,$Temp,$MemTemp,$DataType,$Offset[5]=["nop","nop","nop","nop","nop"]If$ToDo<>"Read"And$ToDo<>"Write"ThenMsgBox(0,"ошибка","неправельный параметр");Определяем массив в который можно записать до 4 массивов. Сразу же присваиваем всем ячейкам какое-нибудь значение отличное от офсета.;$Base - сюда скрипт записывает необходимый базовый адрес для текущего запроса;$MemTemp - тут скрипт обрабатывает данные получаемые из памяти необходимые для текущего запроса;$DataType - сюда скрипт записывает тип данных необходимый для текущего запроса;$ToDo - что мы хотим делать по данному адресу;$Data - если мы хотим произвести запись по адресу, мы должны указать что мы хоти туда записать.;При обращении в эту функцию, она сперва переопределяет все значения на необходимые скрипту в данный момент и далее отправляет эти данные на обработкуSelectCase$Param1="Char";наша структура работы с персонажем Обращение: DataOp("Char", ...$DataType="dword";тип данных$Base=$baseADDR+0x005D4B64SelectCase$Param2="Cur HP";жизни персонажа. Обращение: DataOp("Char", "Cur HP")$Offset[1]='4'$Offset[2]='1BC'Case$Param2="Max MP";макс жизни персонажа. Обращение: DataOp("Char", "Max HP")$Offset[1]='4'$Offset[2]='494'$Offset[3]='324'Case$Param2="Cur MP";мана персонажа. Обращение: DataOp("Char", "Cur MP")$Offset[1]='4'$Offset[2]='494'$Offset[3]='320'Case$Param2="Max HP";мана персонажа. Обращение: DataOp("Char", "Max MP")$Offset[1]='4'$Offset[2]='174'Case$Param2="Pos";Обращение: DataOp("Char", "Pos", ...$Base=$baseADDR+0x005D4B64$DataType="float"SelectCase$Param3="X";Обращение: DataOp("Char", "Pos", "X")$Offset[1]='1B8'$Offset[2]='34'Case$Param3="Y";Обращение: DataOp("Char", "Pos", "Y")$Offset[1]='1B8'$Offset[2]='38'Case$Param3="Z";Обращение: DataOp("Char", "Pos", "Z")$Offset[1]='1B8'$Offset[2]='3c';~Case$Param3="R";Обращение: DataOp("Char", "Pos", "R Base") Работаем с ячейкой отвечающей за ротацию чара$Offset[1]='1B8'$Offset[2]='24'Case$Param3="All";Обращение: DataOp("Char", "Pos", "All")Local$rScan[4]=["X","Y","Z","R"];Создаем временный массив в который заносим то что будет сканироваться и в каком порядке оно будет сканироватьсяLocal$rTemp[4];Создаем временный массив в который будем записывать результаты скановFor$i=0ToUBound($rTemp,1)-1Step1$rTemp[$i]=DataOp("Char","Pos",$rScan[$i]);СканируемNextReturn$rTemp;Возвращаем массивCaseElseSetError(3);Сообщение об ошибке может быть закомменчено или наоборот, в зависимости от того чего мы хотим от скрипта.;~ ErrorMsg('DataOp("'&$Param1&'", "'&$Param2&'", WTF?...', 'Ошибка вызова: команды DataOp("'&$Param1&'", "'&$Param2&'", "'&$Param3&'") не существует.')EndSelectCaseElseSetError(2);Сообщение об ошибке может быть закомменчено или наоборот, в зависимости от того чего мы хотим от скрипта.;~ ErrorMsg('DataOp("'&$Param1&'", WTF?...', 'Ошибка вызова: команды DataOp("'&$Param1&'", "'&$Param2&'") не существует.')EndSelectCase$Param1="Target"$DataType="dword";тип данныхSelectCase$Param2="CursorInfo";ж Обращение: DataOp("Target", "CursorInfo")$Base=$baseADDR+0x004BCD74$MemTemp=_MemoryRead($Base,$DllInformation)Return$MemTempCase$Param2="targetCheck";ж Обращение: DataOp("Target", "targetCheck")$Base=$baseADDR+0x005D4B64$Offset[1]='4'$Offset[2]='190'$Offset[3]='E4'CaseElseSetError(2);Сообщение об ошибке может быть закомменчено или наоборот, в зависимости от того чего мы хотим от скрипта.;~ ErrorMsg('DataOp("'&$Param1&'", WTF?...', 'Ошибка вызова: команды DataOp("'&$Param1&'", "'&$Param2&'") не существует.')EndSelectCaseElseSetError(1);Сообщение об ошибке может быть закомменчено или наоборот, в зависимости от того чего мы хотим от скрипта.;~ ErrorMsg('DataOp(WTF?...', 'Ошибка вызова: команды DataOp("'&$Param1&'") не существует.')EndSelect;Тут происходит обработка данных пришедших сверхку$MemTemp=(_MemoryRead($Base,$DllInformation));первый запрос в память, полученный результат уже будет обрабатываться в циклеFor$i=2ToUBound($Offset,1)-1Step1;i начинается с двух потому что было решено оставить [0] ячейку пустой, мб в будущем пригодится. В обращениях же к памяти обращения идут в ячейку [$i-1]IfString($Offset[$i])="nop"Then;Смотрим значение текущей ячейки. Если оно не содержит в себе оффсетIf$ToDo="Read"Then$MemTemp=_MemoryRead($MemTemp+Dec($Offset[$i-1]),$DllInformation,$DataType);Если $ToDo = Read - читаем память. Значит нам пора формировать последний запрос используя данные хранящиеся в пред идущей ячейке [$i-1], на этом этапе нам необходимо указать тип данных которые мы желаем получить. Задаются в переменной $DataTypeIf$ToDo="Write"Then$MemTemp=_MemoryWrite($MemTemp+Dec($Offset[$i-1]),$DllInformation,$Data,$DataType);Если $ToDo = Write - пишем память. Значит нам пора формировать последний запрос используя данные хранящиеся в пред идущей ячейке [$i-1], на этом этапе нам необходимо указать тип данных которые мы желаем записать. Задаются в переменной $DataTypeExitLoop;Отправив последний запрос, выходим из цикла. Нам больше нечего искать.Else;Если значение в текущей ячейке содержит в себе оффсет, значит и пред идущая ячейка содержит в себе оффсет.$MemTemp=(_MemoryRead($MemTemp+Dec($Offset[$i-1]),$DllInformation));Формируем запрос используя оффсет хранящийся в пред идущей от текущей ячейке [$i-1], чтобы попасть на следующий уровень памяти.EndIfNextReturn$MemTemp;Возвращаем полученные данные.EndFuncFunc_MemoryModuleGetBaseAddress($iPID,$sModule)IfNotProcessExists($iPID)ThenReturnSetError(1,0,0)IfNotIsString($sModule)ThenReturnSetError(2,0,0)Local$PSAPI=DllOpen("psapi.dll");Get Process HandleLocal$hProcessLocal$PERMISSION=BitOR(0x0002,0x0400,0x0008,0x0010,0x0020); CREATE_THREAD, QUERY_INFORMATION, VM_OPERATION, VM_READ, VM_WRITEIf$iPID>0ThenLocal$hProcess=DllCall("kernel32.dll","ptr","OpenProcess","dword",$PERMISSION,"int",0,"dword",$iPID)If$hProcess[0]Then$hProcess=$hProcess[0]EndIfEndIf;EnumProcessModulesLocal$Modules=DllStructCreate("ptr[1024]")Local$aCall=DllCall($PSAPI,"int","EnumProcessModules","ptr",$hProcess,"ptr",DllStructGetPtr($Modules),"dword",DllStructGetSize($Modules),"dword*",0)If$aCall[4]>0ThenLocal$iModnum=$aCall[4]/4Local$aTempFor$i=1To$iModnum$aTemp=DllCall($PSAPI,"dword","GetModuleBaseNameW","ptr",$hProcess,"ptr",Ptr(DllStructGetData($Modules,1,$i)),"wstr","","dword",260)If$aTemp[3]=$sModuleThenDllClose($PSAPI)ReturnPtr(DllStructGetData($Modules,1,$i))EndIfNextEndIfDllClose($PSAPI)ReturnSetError(-1,0,0)EndFunc
Re: [Diablo III] Бот для игры, взлом, слезы о банах евро аккаунтов.
Вернемся к нашим баранам. Спустя год после того как я ковырялся с Iris Online и решил взяться за Diablo III, с величайшим ужасом я понял что ничего уже не помню. Попытки разобраться в том что я же и писал, спустя год привели ни к чему т.к. я подзабыл сам принцип работы всех этих статических адресов, базовых адресов, оффсетов и тд и тп. В связи с этим, основываясь на подсказки lirikmel и кучи старых исходников я пришел к выводу, что во избежания подобных ситуаций, надо на будущее выложить примитивнейший пример который последует ниже:
Код:
#include "NomadMemory.au3"global$sModule="Diablo III.exe"global$ProcessID=WinGetProcess("Diablo III")SetPrivilege("SeDebugPrivilege",1)global$DllInformation=_MemoryOpen($ProcessID)global$baseADDR=_MemoryModuleGetBaseAddress($ProcessID,$sModule)$Base=$baseADDR+0x00FF8864MsgBox(0,"Base",hex($Base))local$temp=_MemoryRead($Base,$DllInformation,'ptr');Тут и далее читаем как 'ptr', так как они указывают не на финальное значение а на промежуточный адрес который мы должны увидеть как HexMsgBox(0,"$temp",hex($temp))local$temp2=_MemoryRead($temp+dec('40'),$DllInformation,'ptr')MsgBox(0,"$temp2",hex($temp2))local$temp3=_MemoryRead($temp2+dec('c8'),$DllInformation,'ptr')MsgBox(0,"$temp3",hex($temp3))local$temp4=_MemoryRead($temp3+dec('c'),$DllInformation,'ptr')MsgBox(0,"$temp4",hex($temp4))Local$temp5=_MemoryRead($temp4+dec('294'),$DllInformation,'ptr')MsgBox(0,"$temp5",hex($temp5))Local$temp6=_MemoryRead($temp5+dec('38'),$DllInformation,'float');Финальное значение (Текущий показатель HP). Хранится во 'float'. Читаем так жеMsgBox(0,"$temp6",$temp6)
NomadMemory.au3 взята отсюда: http://autoit-script.ru/index.php/topic,10406.0.html и её версия на момент написания 3.1.127
Короче, там внизу есть картиночка, на ней окно CE с открытым поинтером и 7 квадратных месседж боксов выводимых приведенным сверху скриптом. В заголовках месседж боксов содержится название переменных а внутри их содержимое на момент вывода. Выводить мы собираемся текущий показатель жизни для сорки из игры Diablo III, версия клиента 1.0.2.9991, текущая дата: 19.06.2012.
Не смотря на то что со стороны кажется всё это бедой весьма ректальной и на первой взгляд непонятной, думаю тем кто впервые лезет ковыряться с мультиуровневыми оффсетами к играм процесс в CE у которых выглядит например так: "Diablo III.exe"+00FF8864, поможет это сообщение.
Не знаю что будет с этой картинкой когда это сообщение будете читать вы, но там на картинке есть циферки, красненькие. Они то и отражают логику продвижения кода к искомому значению.
[box title=Длинно:]
1 - "Diablo III.exe"+00FF8864 это "Базовый адрес"+статический оффсет. Их сумма дает нам значение 2.
2 - Это адрес ячейки, прочитав содержимое которой в виде ptr, мы получим значение 3.
3 - Это адрес ячейки, прибавив к которому оффсет "40" (значение 4), мы получим значение 5
5 - это адрес ячейки, прибавив к которому оффсет "с8" (значение 6), мы получим значение 7
7 - Это адрес ячейки, прибавив к которому оффсет "с" (значение 8 ), мы получим значение 9
9 - Это адрес ячейки, прибавив к которому оффсет "294" (значение 10), мы получим значение 11
11 - Это адрес ячейки прибавив к которому оффсет "38" (значение 12) мы получим значение 13
13 - Это адрес ячейки, прочитав информацию с которого во 'float', мы получим искомое значение 14, являющееся показателями текущего ХП чара[/box]
Ниже привожу сделанный на той же самой NomadMemory 3.1.127 кусок кода, выполняющий ту же функцию что и приведенный и расписанный выше, только в место несколько запросов _MemoryRead, мы используем другую функцию содержащуюся в текущей версии, встречайте _MemoryPointerRead. Работает так же, занимает места меньше в коде, КАК оно работает лучше не думать, можно нечаянно сломать мозг. Скажу только что тут у нас все оффсеты записываются в массив, где "нулевая" ячейка не используется и всегда равна нулю. Если любой оффсет равен нулю, в массиве на его месте так и надо указать 0. В принципе можно почитать её описание внутри инклуда, есть шанс что оттуда вы почерпнете что-то полезное.
[Diablo III] X, Y, Z. Расположение координатных осей относительно карты.
Опять у меня появилось время на диабло, поэтому рассмотрим нахождение X, Y, Z координат нашего персонажа. Хитрые близзарды поступили еще хитрее чем я от них ожидал. Когда я искал координаты, я старался делать это привычным образом, начиная с поиска X или Y. Я находил какие-то непонятные числа, измеряемые в тысячах. Я трудолюбиво бегал влево и вправо и искал увеличившиеся и уменьшившиеся значения. Находил да. Но это всегда было два непонятных числа, которые практически на одинаковое количество увеличивались и уменьшались. Я решил плюнуть на это и придумал более легкий способ, я приступил к поиску Z координаты. В первой локации как только вы начинаете играть, есть очень удобный холмик. Бегаем по нему вверх вниз и радуемся. Как водится это число должно быть значительно меньше чем X и Y и когда мы бежим по ровной поверхности практически не должно изменяться. Найти и удостовериться что это именно Z не составило труда, побегав по холмику и потом по ровной поверхности. Еще даже не приступая к поиску оффсета на эту координату, я довольный собой решил просмотреть регион памяти вокруг этого значения. Велико же было моё удивление, когда там я обнаружил те два злосчастных числа которые дружно вдвоем вылазили мне перед глазами когда я пытался найти X и Y. Убедившись в том что действительно это они самые и были, я приступил к поиску оффсета на Z. Найдя его я добавил оффсеты на X и Y. Но еще ближайшие пол часа я не понимал какое из этих чисел к какой оси относится. Из опыта других игр, я думал что в памяти они лежат в виде 1-я ячейка = X, 2-я = Y, 3-я = Z. Не тут то было. В какую бы сторону по горизонтали я не бежал, как бы ровно по одной оси бежать я не пытался, менялось всегда оба числа.
Я решил заглянуть в кабак и побегать там по стенкам. Результатом был я удивлен чуть больше чем очень сильно. То что я думал является X и Y, на самом деле являлось Y и X. И как я выяснил обойдя все углы кабака, оси то наклонены, причом не слабо и направление оси Y изменено. Прикладываю листок который помог мне определить это и новую таблицу с оффсетами для патча 1.0.3.10057.
Belfigor, а от чего вы отталкивались когда искали координаты X Y Z в игре ? С чего начинали поиск. С единицы с нуля ....или еще с чегото ...можно немного рассказать ?
Belfigor, а от чего вы отталкивались когда искали координаты X Y Z в игре ? С чего начинали поиск. С единицы с нуля ....или еще с чегото ...можно немного рассказать ?
Scan Type: Float
Value Type: Unknown Initial Value
А далее, забежал на горку: Increased Value
Постоял на месте не двигаясь: Unchanged Value
Спустился с горки: Decreased Value
и так пока не осталось минимум вариантов.
Мда но расположение оси координат вполне стандартное для изометрических игр -))(узнаю например MU online )
И кстати вопросик как вводятся офсеты в функцию _MemoryPointerRead
Так же как мы ранее вводили их построчно и читали адреса по очереди, только теперь в строку в массиве где первая ячейка = 0. Порядок оффсетов сохраняется таким же. Для окна CE это снизу вверх, для нас это 1-й = 0, остальные слева направо.
Добавлено:
Сообщение автоматически объединено:
Сравни 2 выложенных мною выше куска кода. Оба были рабочие на прошлом патче. Время появится перепишу под текущий патч и напишу как их вообще адаптировать под текущие патчи, чтобы пример был актуален всегда.
Я пишу хелпера под ботов, ака старт-стоп-рестарт, контроль статуса, и так далее.
В Вовке все просто, давно набросал себе шаблон и только меняю оффсеты.
Хотелось бы такое адаптировать под д3, но нужна помощь.
Начать с простого - получать статусы какие-то с игры. Онлнай чар, или в меню, хп статус, и так далее.
Буду признателен за помощь.
Код:
#include "NomadMemory.au3"
SetPrivilege("SeDebugPrivilege", 1)
global $sModule = "Diablo III.exe"
global $ProcessID = WinGetProcess("Diablo III")
MsgBox(0,"$ProcessID", $ProcessID)
SetPrivilege("SeDebugPrivilege", 1)
global $DllInformation = _MemoryOpen($ProcessID)
MsgBox(0,"DLL",$DllInformation)
global $baseADDR = _MemoryModuleGetBaseAddress($ProcessID, $sModule)
MsgBox(0,"$baseADDR",$baseADDR)
$Base =$baseADDR+0x00E40D94
MsgBox(0,"Base",hex($Base))
Global $TargetHP_cur_offset[6] = [0, 0x40, 0xC8, 0xC, 0x294, 0x38]
$test = _MemoryPointerRead($Base, $DllInformation, $TargetHP_cur_offset, "float")
MsgBox(0,"$HP_Cur",$test[1])
В этом примере он не может определить хп игрока., я так понимаю нужно обновлять значения $TargetHP_cur_offset[6] = [0, 0x40, 0xC8, 0xC, 0x294, 0x38] . Что это такое и где их брать? Как они находятся. Можно поподробнее?
да но разве не их постоянно выкладывают в теме на овнеде? Я имею ввиду может у этих офсетов есть название иное, чтобы их можно было тягать с тем где выкладывают все оффсеты.