-
Постов
2 999 -
Зарегистрирован
-
Победитель дней
129
Тип контента
Профили
Форумы
Загрузки
Блоги
Сообщения, опубликованные MasterGH
-
-
Ближе к концу на максимальной сложности ... там где зеленые "шары" вылазят из нор. У меня было около 5 патронов дробовика и с сотню от винтовки похожей на СВД с лазерным прицелом и играл без читов по часа два в день начиная где-то с даты создания этой темы.... Короче получалось либо я далеко убегал от напарника бегая от шаров и напарник умирал от них... либо мой герой умирал потому, что я всё защищал напарника. Так что раз 15 - и я забил на игру
Сегодня нашёл адрес напарника и заморозил его .... и далее ещё около трёх уровней пройти и я лицезрел список авторов игры оповещающих о её конце (не ожидал что она такая короткая). А дальше какая-то непонятная пауза - хоть игру отрубай.
-
Хм... интересное дело с ножами.
Можно представить массив указателей объектов которые могут быть на карте. У этих объектов должны быть три координаты и идентификатор типа объекта: это данные должны быть у всех подобных объектов.
Например:
Нож:ID1
Стрелы:ID2
Массив: ID1,ID1,ID2,ID1,ID2,ID1...
Есть несколько вариантов как сделать респавн:
1. Вариант:
На создание и добавление в этот массив объектов не влияет наш игрок, а влияет только сценарий начала уровня. Т.е. только сценарий создаёт и кладёт в этот массив объекты, а те кто их юзает могут только юзать и максимум выкинуть из массива (уничтожить, но не создать).
Вопрос здесь стоит только в том, чтобы выйти на место кода игры где происходит создание объекта... а дальше уже будет видно.
2. Вариант.
Данный массив используется иначе. Каждый объект игрока создаёт локальные копии объектов, которые он берёт из массива. При этом объекты в этом массиве разрушаются.
В этом случае можно зациклить сбор нажей с одного "поднимаемого" объекта ножа из массива. Т.е. когда игрок берёт один нож, то мы зацикливаем например на 200 копий, после чего один объект разрушиться из массива, копию которого мы делали. Такой вариант был СТАЛКЕРЕ с патронами.
Оба варианта сводятся к тому чтобы выйти на массив объектов и понять какой из двух вариантов следует. Мне почему-то кажется, что есть ещё один вариант.
3. Вариант.
Он похож на второй. Берём объект из массива "мира" создаём его копию, которую помещаем в создаваемый объект управления ножом. Таким образом Указатель на управление ножом кладём в объект героя...
Вот такие мысли...
-
Интересная информация ))
По поводу бессмертных дружественных NPS у меня есть одна идея. Зацепка от которой можно оттолкнуться, просто мелочь...
Всем кто играл известно что в дружественных NPS нельзя стрелять - наводишь прицел на "друга" пытаешься выстрелить и не получается. А это значит, что перед выстрелом происходит сравнение свой/чужой. Осталось только найти данные которые задействованы в сравнении, а дальше в инструкцию работающую со здоровьем игроков внедрить условие, которое дружественным NPS пишет постоянное здоровье..
Пока такие мысли..
-
Всё понятно, спасибо за ссылки... Я добавлю краткое описание в первый пост.
-
Кто-нибудь знает хакнули фильтры от противогаза или нет и что там с гранатами(динамитом)... они как оформлены как объекты? И по какому принципу супер прыжок работает...
aliast, ты на плейграунде писал, что не смог сделать дружественных игроков бессметрными, а можно узнать узнать поконкретней в чём проблема была?
На плейграунде нужно подождать с чуть меньше недели, чтобы твой трейнер добавили ))) Те кто добавляют материал "там еле чешутся"
----
Короче, я так понял там гранаты, фильтры и метальные патроны нужно спавнить(создавать) через конструктор. Я месяца два назад научился спавнить патроны в СТАЛКЕРЕ, но всё руки "не доходят" написать как это сделал.
В Метро можно попробовать найти адрес счётчика метальных патронов и найти инструкции работающие с ним при перезарядке. При перезарядке должно происходить создание объектов в стволе оружия. Самый простой вариант сделать так, чтобы при перезарядке создавались в стволе не существующие патроны, но в этом случае пользователю постоянно придётся перезаряжаться. А нужно сделать так, что при выстреле инициализировалась перезарядка с отсутсвием или с очень быстрой анимацией перезарядки, тогда будет сделан так называемый чит-спавн патронов. Почти аналогично касается предметов которые игрок подбирает с трупов, в этот момент нужно узнать прерывающиеся инструкции, затем узнать в какую функцию они входят и какие параметры этой функции должны быть. Затем задать эти параметры с вызовом функции (либо внутренним потоком игры из некоторого места либо созданным потоком CE через скрпты) и будет спавн динамита или некоторого предмета
Но это в теории...
Я сейчас читаю посты темы...
Ты писал
movss xmm0,[eax+000001e4]comiss xmm0,[_dead] //с этой строкой игра виснет. И без неё тоже (при попытке объявить переменную _dead)
// comiss xmm0,[agpmeventloggingenabled+3fcff4] //сравнение с нулём - с этой строкой игра не виснет
Данные (если я не ошибаюсь) должны быть выровнены от начала блока памяти кратно 16 байт, ещё говорят выровнены по 16-му параграфу. Назначь метку _dead, от метки _newmem на расстоянии кратном 16-ти байтам.
-
Бывают ещё сообщения:
strhappybirthday='Let''s sing Happy Birthday for Dark Byte today!';strXMess='Merry christmas and happy new year';
strNewyear='And what are your good intentions for this year? ;-)';
strfuture='Wow,I never imagined people would use Cheat Engine up to today';
Если в образовательных целях, то сообщение с первоапрельской шуткой вылазит здесь..
[cod]procedure TMainForm.FormShow(Sender: TObject);
resourcestring
strhappybirthday='Let''s sing Happy Birthday for Dark Byte today!';
strXMess='Merry christmas and happy new year';
strNewyear='And what are your good intentions for this year? ;-)';
strfuture='Wow,I never imagined people would use Cheat Engine up to today';
var reg: tregistry;
modifier: dword;
key: dword;
hotkey: string;
year,month,day: word;
temp:string;
i: integer;
outputfile: textfile;
go:boolean;
loadt: boolean;
firsttime: boolean;
x: array of integer;
begin
Set8087CW($133f);
loadt:=false;
edit2.Text:=format('%.1f',[1.0]);
reg:=Tregistry.Create;
try
Reg.RootKey := HKEY_CURRENT_USER;
if not Reg.OpenKey('SoftwareCheat Engine',false) then
begin
if Reg.OpenKey('SoftwareCheat Engine',true) then
begin
//write some default data into the register
reg.WriteBool('Undo',true);
reg.writeBool('Advanced',true);
reg.writeBool('SeperateThread',true);
reg.writebool('Use Hyperscan if posible',false);
reg.WriteInteger('ScanThreadpriority',formsettings.combothreadpriority.itemindex);
end;
end;
except
end;
if reg.ValueExists('First Time User') then
firsttime:=reg.ReadBool('First Time User')
else
firsttime:=true;
if firsttime then
begin
reg.WriteBool('First Time User',false);
if messagedlg('Do you want to try out the tutorial?',mtconfirmation,[mbyes,mbno],0)=mryes then
shellexecute(0,'open','Tutorial.exe','','',sw_show);
end;
// animatewindow(mainform.Handle,10000,AW_CENTER);
//mainform.repaint;
fronttext:='brings Cheat engine to front';
if dontrunshow then exit;
dontrunshow:=true;
decodedate(now,year,month,day);
if (month=7) and (day=1) then showmessage(strhappybirthday);
if (month=12) and ((day=25) or (day=26)) then Showmessage(strXmess);
if (month=1) and (day=1) then showmessage(strnewyear );
if (month=1) and (day=1) and (year>=2010) then showmessage(strFuture);
if (month=4) and (day=1) then aprilfools:=true;
if aprilfools=true then
Messagedlg('Your license to use Cheat Engine has expired. You can buy a license to use cheat engine for 1 month for $200, 6 months for only $1000 and for 1 year for only $1800.'+' If you don''t renew your license Cheat Engine will be severely limited in it''s abilities. (e.g: Next scan has been disabled)',mtwarning,[mbok],0);
LoadSettingsFromRegistry;
//Load the table if one was suplied
overridedebug:=false;
if paramcount>=1 then
begin
loadt:=true;
if uppercase(paramstr(1))='-O' then
begin
OverrideDebug:=true;
loadt:=false;
end;
if paramcount>1 then
begin
//extra param
temp:=paramstr(2);
if length(temp)>4 then
if temp[1]+temp[2]+temp[3]='/o=' then
begin
//output to:
temp:=copy(temp,4,length(temp)-3);
assignfile(outputfile, temp);
rewrite(outputfile);
for i:=0 to numberofrecords-1 do
writeln(outputfile,memrec.description+' '+IntToHex(memrec.Address,8));
closefile(outputfile);
showmessage(temp+' has been created');
end;
end;
end else
begin
end;
if loadt then
begin
LoadTable(paramstr(1),false);
updatescreen;
updatelist;
end;
if GetSystemType<3 then //not a supported os for hardware breakpoints
with formsettings do
begin
rdWriteExceptions.Checked:=true;
rbDebugRegisters.Enabled:=false;
label6.Enabled:=false;
label7.Enabled:=false;
rbDebugAsBreakpoint.Enabled:=false;
rbInt3AsBreakpoint.Checked:=true;
end;
if (GetSystemType<4) {or (is64bitos)} then //not nt or later
begin
with formsettings do
begin
cbKernelQueryMemoryRegion.enabled:=false;
cbKernelReadWriteProcessMemory.enabled:=false;
cbKernelOpenProcess.enabled:=false;
cbProcessWatcher.Enabled:=false;
cbKDebug.enabled:=false;
cbGlobalDebug.enabled:=false;
TauntOldOsUser.Visible:=true;
label25.Enabled:=false;
end;
end;
vartypechange(vartype);
adjustbringtofronttext;
if aprilfools then
caption:=cenorm+' EXPIRED!';
if autoattachtimer.enabled then autoattachcheck;
//SMenu:=GetSystemMenu(handle,false);
//don't put this in oncreate, just don't
if memscan=nil then
memscan:=tmemscan.create(progressbar1);
memscan.setScanDoneCallback(mainform.handle,wm_scandone);
FileAccessTest;
end;[/cod]
Оно мне уже надоело своим появление и я его кильну на... так же как и предложение потренироваться при первом запуске (при сбросе настроек в реестре)
-
Все версии можно будет скачивать с нашего сайта.
ПЕРЕЙТИ Cheat Engine 5.6 RUS (1.0)
1. Модернизированы окна: главное, окно прервавшихся инструкций.
2. Переведены окна: главное, отладчика, спасибо aliast-у за перевод (настроек, туториала)
3. Автоматическое выравнивание столбцов в окне отладчика.
3. Добавлена функция отчётности прервавшихся инструкций там же возможность ставить галочки чтобы отмечать инструкции
4. Добавлена Функция отключения отладчика
5. Исправлены ошибки при удалении записей в трейнеросоздателе - ошибка характерна и для оригинальной версии
6. Шаблон в автоассемблере генерируется с учётом сканирования сигнатур.
7. Добавлена функция показывающая загруженный игрой и системой файлы (для определения сейв-файлов)
8. Добавлена функция разложения указателей из CE 5.5
9. В инсталяторе есть лоадер, который сбрасывает настройки (правда немного он не продуман, из-за него запускается каждый раз предложение пройти тренировку, в будущем исправлю)
10. Инсталлируемый файл CheatEngine.exe переименован в другой с пометкой что это русская версия и в нём указана версия.
11. Также вы можете без опасений денисталировать файлы, который были установлены. Ничего не перезаписывается и не удаляется, того что удалять нельзя.
ps. Эту версию я уже выкладывал, т.к. что скорее кому было надо те скачивали.
-
1
-
-
Здесь пишем баги.
Пишу о тех которые я нашёл:
Статус: не исправлено
* В окне структур при задании нового типа некотрому смещению - значение в соответсвии с типом не меняется.
Статус: не исправлено
* Не работает сгенерируемый трейнер при активации скриптов по горячим клавишам
Статус: не исправлено
* Проверить: "А у всех CE перестаёт работать (искать адреса и активировать скрипты) после запуска сканера указателей? шибка возникает вроде бы когда указателей сильно много находится. Иначе всё работает. Наример, в той же операции флэшпоинт или метро 2033 ошибка "Scan error:thread 0:Out of memory""
Статус: не исправлено
* Баг в при ассемблировании.
Я узнал опытным путём, что ассемблирование происходит с добавлением лишних байт
00D25EA5 - 8d 04 2f - lea eax,[edi+ebp] //оригинальная инструкция00D25EA5 - 8d 44 2f 00 - lea eax,[edi+ebp+00]
Даже если вы не пишите "lea eax,[edi+ebp+00]", а "lea eax,[edi+ebp]", то всё равно у вас будет "lea eax,[edi+ebp+00]".
А в этом случае меняются первые байты.
00D6E729 - a3 08 27 9c 02 - mov [029c2708],eax //оригинальная инструкция00D6E729 - 89 05 08 27 9c 02 - mov [029c2708],eax
Ну жесть просто. При отмене чит-кода с такими инструкциями у вас будет "крах игры"
-
Статья GTA Vice City - это статья с которой я нулевыми знаниям начал путь в advanced gamehacking. А затем F.E.A.R. - тоже произвела впечатление. Три года прошло и если сравнить знания мои до того как я прочёл эти статьи с теми, что сейчас, то можно посмеяться ))) Тогда я смотрел на ассемблерные инструкции типа этого
и понятия не имел как с этим работать.... жесть ))) Теперь настало время и я прокомментирую моменты, которые были мне непонятными или которые нужно дополнить. Надеюсь Xipho будет интересно. Комментарии мои могут быть сложным новичкам, но что тут поделаешь, написал как написал...
Для удобства я все картинки вытащил из под спойлеров, а комментарии я буду писать красным. Поехали...
Скрытый текстЦитатаБессмертие только для игрока с использованием метода указателей Сложность: НовичокИнструменты: Artmoney, Tsearch.
Итак, начнем. Запускаем игру, запоминаем количество жизней:
сворачиваем игру и запускаем Artmoney и выбираем игру из списка процессов.
Далее делаем поиск:
Почему именно так? Да потому, что жизни в вайсе хранятся в переменной с плавающей точкой.
Позволю себе отметить, что этот финт поиска данных был известен не каждому. Тогда в той ещё Артмани 7.13 также не было корректировок округления вещественных чисел в настройках. Но увидив данный приём однажды, можно было его применять в других играх.
Далее делаем что-нибудь, чтобы потратить немного жизней, например падаем с крыши невысокого здания (чтобы не насмерть и чтобы жизней осталось на еще два-три таких падения)
Далее делаем отсев с получившимися жизнями по принципу скриншота… то есть диапазон значений вычисляется так:
нижний порог диапазона = текущие жизни-1
верхний порог диапазона = текущие жизни+1
Повторяем отсев до тех пор, пока не останется ОДИН адрес. Вот что получилось у меня в итоге:
В игре:
А в Artmoney:
Как видим, в игре у нас 66 процентов, а в АртМани 66.02 Это потому, что игра просто отбрасывает сотые доли и пишет в жизни тока целую часть. Наверное это сделано для затруднения взлома игры. Ну да нам это не помеха
Запускаем TSearch:
Эта программа Хит своего времени (как и MTC) и сейчас может использоваться, но CheatEngine более гибкая и мощнее и также MHS
и открываем процесс игры. Надеюсь с этим вы справитесь без меня... Далее включаем отладчик:
Минус в удобстве TSearch - cпрашивается зачем эти лишние действия, когда отладчик можно было бы включать автоматом при попытке поставить бряк на адресе. Дальнейший путь установки бряка также можно было бы сократить, как это сделано в CE
и открываем AutoHack window. Тоже надеюсь, что найдете где
В этом окне нажимаем зеленую стрелочку:
Эта стрелочка позволяет установить breakpoint (точку останова) на адрес наших жизней:
Это наш адрес жизней найденный в АртМани, вставляем его в окно установки breakpoint’a:
Обратите внимание на тип breakpoint’a: он должен быть открытым, то есть срабатывать и на чтение из этого адреса и на запись.
Многое остаётся за кадром. Почему именно "и на чтение и на запись"?! Ведь сразу после этого идёт следующий абзац и мысль как будто обрывается.
Пояснение. Бряк и на чтение и на запись, так называемый "открытый" брейкпоинт или бряк на доступ позволит найти предполагаемую частообращаемую инструкцию к адресу здоровья. Обычно, эта инструкция типа чтения, а иногда и на запись. О частоте обращения можно только догадываться, т.к. TSerach не считает кол-во обращений. Однако, обычно, частообращаемые инструкции являются инструкциями чтения, которые работают в игре, когда игрок даже ничего не делает. Руководствуясь данным принципом мы можем найти ту самую частообращаемую инструкцию к адресу здоровья и внедрить чит-код в неё и он будет выполняться создавая эффект постоянного здоровья.
Мы также могли бы внедрить в некоторую инструкцию записи из появившихся, когда игрок получил урон, но как показала моя практика, проще внедрять так, как тут описано, хотя случаи бывали разные. Перед вами может стоять выбор:
1. внедрить в одну частообращаему инструкцию,
2. внедрить в одну или несколько инструкций записи, которые срабатывают обычно при уроне,
3. внедрить на ваш выбор, куда угодно и что угодно, лишь бы чит работал должным образом
Далее мы возвращаемся в игру и бегаем пару секунд, но жизни НЕ тратим! Да, и в артмани их не замораживаем!
После возврата в TSearch видим, что наше breakpoint сработал, и еще как сработал:
Когда я читал первый раз статью, я точно помню, что задавался вопросом: "Почему именно так нужно поступать?". Как будто Xipho взял да и угадал что нужно сделать.
Как я уже писал, мы стараемся найти часто обращаемые инструкции (т.к. выбрали этот путь внедрения) и они обычно срабатывают, когда мы просто зашли и вышли из игры. Если мы не будем тратить жизни, то у нас будет меньше инструкций обращения, которые срабатывают только при уроне. Поэтому подмеченное "НЕ" тратим жизни, нужно только для того чтобы не работать с лишними инструкциями (которые срабатывают только при уроне), т.к. нам нужным часто обращаемые.
Последний из найденных сразу же отсеиваем, так как это сравнение жизней, оно нам не нужно.
Ну, а тут я могу поспорить. "Сравнение жизней" это не аргумент. Прежде всего это предполагаемая часто обращаемая инструкция и в неё можно внедрить условие и оно должно будет работать не хуже. Роль данной инструкции сравнить некотрое число с точкой со значением здоровья. После сравнения или перед сравнением можно внедрить чит-код.
Также хочется отменить, что именно сейчас мы имеем возможность охарактеризовать частично структуру героя в игре. Начало структуры является значение регистра, а смещение +0x354 - это велечина от начала структуры по которой находится здоровье.
Наш игрок или не наш игрок имеет такую же структуру с таким же смещением здоровья. Некторые инструкции которые мы видели, работают как с нашим игроком так и не с нашими. Хотя точно утвержать, пока не установим это, мы не можем.
Установив какие инструкции работают только со структурой вашего игрока вы можете сделать чит-код с записью постоянного здоровья по смещению +0x354 чем избавите себя от лишних хлопот. Если инструкций работающих только со структурой вашего героя не будет, то придётся изменять инструкции делая фильтр "является ли регистр структурой вашего игрока". Отличить вашу структуру от структуры ботов можно, например, с помощью указателя на структуру вашего героя, который придётся искать далее.
С этого места можно некоторым образом начать понимать какие инструкции вам необходимо стремиться изменять - часто обращаемые и работающие только со структурой вашего героя. Правда я ещё подстраховываюсь и изменяю также хотябы одну инструкцию урона в случаях, когда урон может отнять всё здоровье героя. Имел ли место быть такой случай в этой игре я без понятия.
А вот остальные придется перебирать вручную. Я этого делать не буду, дабы не затягивать туториал, и сразу покажу какой работает на моей версии игры, так как я ее уже ломал
Сложно предположить по какому критерию был перебор вручную Все инструкции теоретически вполне подходят под критерий внедрения. Сдаётся мне что на всех инструкциях нужен фильтр, а Xipho искал, ту которая работает без фильтра
Для начала найдем дырку в коде, для вставки нашей процедуры. Я использую для этой цели CodeCaver:
Это было давно, сейчас Xipho использует выделение памяти, а не "дырку". Что по сути правильно учитывая разные тонкости, о которых можно узнать поискав по форуму.
я воспользуюсь отмеченным адресом, так как в нем много пустого места...
Включаем в TSearch очень удобную фишку Easy Write:
Затем нажимаем new:
Получаем такое окно:
В верхней части окна пишется код для патча адресов игры, а в нижней – для восттановления исходных значений, если вдруг вы решили поиграть честно
EasyWrite не такой уж Easy в универсальности. Он не сможет понять адрес "Game.dll+5454:", он не приспособлен к выделению памяти, он не может искать адрес внедрения по цепочке байт. И основная фича за которую его любили или до сих пор любят это генерация байт-кода, который на мой взгляд уже утратил свою актуальность. Зачем заниматься компиляцией байт-кода, копипастами и т.п. Берём ассемлерный скрипт, вставляем в трейнер и юзаем без лишней работы с poke-ами. Это можно сделать в скриптах Cheat Engine и непосредственной компиляцией ассемблерных инструкций в среде программирования с их копированием в процесс игры в выделеную память - однако это надо писать особый код трейнера, что не каждый умеет
В верхней части набираем такой код:
Сразу даю пояснения…
fld dword ptr [ecx+0x354] – восстанавливаем оригинальную инструкцию
mov dword ptr [ecx+0x354],0x4479C000 – записываем 999.00 в адрес жизней
jmp 0x501979 возврат в игровую рутинуOffset 0x006E794F --- помните, я выбрал этот адрес для своей процедуры
Здесь более правильнее было бы
Offset 0x006E794F
mov dword ptr [ecx+0x354],0x4479C000
fld dword ptr [ecx+0x354] // загружаем в ST(0) значение 0x4479C000
jmp 0x501979
jmp 0x006E794F – делаем переход на свою процедуру nop – так как оригинальная инструкция всего 6 байт длиной а переход 5 байт длиной, то нам надо вставить это, для баланса байт, иначе получим вылет из игры.Offset 0x501973 – инструкция по этому адресу читает жизни
А в нижней вставляем это:
fld dword ptr [ecx+0x354]Offset 0x501973
То есть восстановление оригинального кода игры, для честной игры. Включив эту опцию, возвращаемся в игру. Мы стали бессмертными
Но что это? Все прохожие и менты тоже стали бессмертными. Вот тут и начинается основная часть нашего взлома.
Ну, вот... похоже нет инструкции, которая работает только с героем. Если вы хотите узнать работает ли инструкция только с вашим героем, то на этой инструкции (Offset 0x501973) нужно определить адрес здоровья врага. Поставить на него также бряк и сравнить какие инструкции срабатывают у него и у вашего героя. Найти отличные и проверить их на практике.
Дальеш пойдёт поиск указателя для создания фильтра, чтобы по нему записать здоровье только нашему герою.
Выключаем бессмертие. В окне AutoHack выбираем вкладку Дизассемблера.
Затем делаем все четко по скриншотам:
Этим мы заставим TSearch смотреть, что же происходит в регистрах при выполнении данной инструкции. Далее кликаем на Register:
И видим такое окно:
Проставляем регистры как на скриншоте и включаем наблюдение (красная рожица слева). Возвращаемся в игру. В игре НИ В КОЕМ СЛУЧАЕ НЕ ВСТУПАЕМ НИ В КАКИЕ РАЗБОРКИ И НЕ ТРАТИМ ЖИЗНИ!!!
Возможно, что если мы потратим чьи-то жизни, то в регистре ecx будет начало структуры того бота, здоровье которого мы отняли в последнюю очередь. В любом случае это подстраховка.
Бегаем пару сек и снова возвращаемся в это окно. Вот и нашли значение для поиска указателя (если вы помните, это и есть цель этого тутора):
Далее переводим его в десятичную систему (калькулятором все пользоваться умеют? ). И ищем как обычное число в артмани… А можно сразу искать его в шестнадцатиричном. Вот так:
то есть мы вводим в строку поиска шестнадцатиричное значение, и просто напросто подставляем в конце h чтобы дать АртМани понять, что это шестнадцатиричное значение. Скорее всего адресов найдется много. Выходим из игры, АртМани и TSearch не закрываем! Снова заходим в игру и идем в TSearch. Снова выбираем процесс, Включаем отладчик в TSearch’e и переходим в окно Автохака. Оно должно было оставаться открытым. Выключаем наблюдение, снова включаем, переходим в игру, бегаем пару секунд и снова возвращаемся в Автохак
А вот тут есть сюрпризы.
1. Указатель может быть в dll - ке, а это не было проверено в статье.
2. Указатель может быть многоуровневым и должен был быть проверен на статичность. Находится ли этот указатель в статичной памяти? Зелёный ли он в CE или MHS...
Видим, что регистр изменился… Делаем отсев по этому значению в АртМани… Повторяем заново, до тех пор, пока не останется только один адрес… Если же после 5-6 отсеиваний адресов все равно несколько, то берем любой
Здесь я думаю стоило жирным выделить очень полезный финт - способ, которым был найден этот указатель. Именно этот способ помимо реверсинга лежит в основе поиска любого указателя, хоть многоуровневого при этом уровни указателей должны изменять своё положение, чтобы их можно было идентифицировать.
Я взял адрес 0078F7E4 он мне больше всего понравился:
Тут надо было обратить внимание, что этот выбор был ориентирован не спроста: 0x201538 лежит ниже 0x00400000, чего не должно быть. Плюс нужно было убедиться в какой памяти лежит 78F7E4 в статической или в dll
Вот практически и все! Дорабатываем наш код:
(Объясняю только то, что изменилось)
Offset 0x006E794F
fld dword ptr [ecx+0x354]
push eax – сохраняю регистр на всякий случай
mov eax,[0x0078F7E4] – считываю из нашего указателя адрес для сравнения
cmp ecx,eax – сравниваю с текущим указателем
pop eax – восстанавливаю регистр (а вдруг он используется)
jne 0x501979 – если указатели не равны, то возвращаюсь без записи
mov dword ptr [ecx+0x354],0x4479C000 – записываю жизни
jmp 0x501979 возвращаюсь в игру.
Можно было написать идеально как-то так:
Offset 0x006E794F
fld dword ptr [ecx+0x354]
cmp ecx,dword ptr[0x0078F7E4]
jne 0x501979
mov dword ptr [ecx+0x354],0x4479C000
jmp 0x501979
Вообще-то после "mov dword ptr [ecx+0x354],0x4479C000" должна идти "fld dword ptr [ecx+0x354]" и также сохранены и восстановлены флаги pushf и popf. Но раз игра позволяет значит, можно так оставить.
А это все без изменений
Offset 0x501973
jmp 0x006E794F
nop
Вот и все!!! Теперь бессмертны только мы!!!
Я бы ещё уточнил - бессмертны благодаря внедрению чит-кода в инструкцию, в котором был фильтр на указатель структуры игрока. Только поэтому бессмертны только мы.
Хотел бы отменить ещё одну важную штуку. Указатель на здоровье теперь можно оформить как
[[0078F7E4]+0x354] = 100.0
В статье не нашли инструкцию работающую только со здоровьем героя на уровне [xxx+0x354], а
в этом случае можно поставить бряк на доступ (открытый) на адрес [0078F7E4] и тогда в большинсве случаев можно найти инструкцию, которая работает только со структурой главного героя. Можно внедрить в эту инструкцию без сравнения (без фильтра) с указателем героя как было описано в статье
Заключение.
На момент выхода эта статья была актуальна, но сейчас уже появились инструменты, более удобные в использовании. В ближайшем будущем данная статья будет переписана с использованием современных инструментов.
-
От лица Администрации поздравляю всех с первым выходом сайта.
Позже кто-нибудь из администрации уточнит для желающих-реализёров, что будем публиковать на сайте и как это будет происходить.
-
Интересное "открытие".
Всё больше и больше становится понятно как разработчики пишут игры.
-
Привет, это вновь MasterGH с занудной, но полезной статьёй )
Рассмотрим работу с некоторыми FPU инструкциями компилятора Borland C++
Подопытная программка, которую я написал выглядит так:
рис.1
(весит 12 кб в сжатом состоянии.. хе-хе, буду знать)
Как видите, она умеет уменьшать, увеличивать и уменьшать с условием значение здоровья типа float.
Основной код, который я добавил в приложение.
float fhealth = 100.0;
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
fhealth-=20.0;
updateValue();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
fhealth+=20.0;
updateValue();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
if (fhealth>10) {fhealth-=5;};
updateValue();
}
void TForm1::updateValue()
{
Edit1->Text=fhealth;
}
//---------------------------------------------------------------------------Остальной код писать не буду, т.к. он генерируется автоматически. Ах да updateValue() нужно добавить в приватную область класса формы и больше я никакого кода не писал. А теперь взглянем на ассемблерные инструкции этих действий.
Буду приводить кусок исходника и описание.
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
fhealth-=20.0;
updateValue();
}рис.2
Так как всё тут просто комментировать не буду.
Идём к следующей инструкции.
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
fhealth+=20.0;
updateValue();
}Почти аналогично, только с инструкцией добавления:
рис.3
Ну, а теперь самое интересное:
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
if (fhealth>10) {fhealth-=5;};
updateValue();
}А теперь разберём инструкции подробнее:
Unit1.cpp.32: if (fhealth>10) {fhealth-=5;};
00401CF8 D90598414000 fld dword ptr [$00404198] //грузим в стек FPU 100.0
00401CFE D9052C1D4000 fld dword ptr [$00401d2c] // грузим в стек FPU 5.0
//Сейчас ST(0)=10, а ST(1)=100
00401D04 D9C9 fxch st(1) // меняет местами содержимое регистров
//Сейчас ST(0)=100, а ST(1)=10
00401D06 DAE9 fucompp // сравнивание с двойным выталкиванием из стека
//Сейчас ST(0)=Empty, а ST(1)= Empty (что означает пустые)
Флаги FPU поменялись
Было/стало
рис.4
00401D08 DFE0 fstsw ax
Лезу в справку:
fstsw и fnstsw сохраняют текущее значение слова статуса FPU в указанном месте. Операндом-адресатом может быть либо 16 бит в памяти, либо регистр AX. fstsw перед сохранением слова проверяет на подвешенные немаскируемые прерывания, fnstsw этого не делает
Короче какие-то флаги FPU загнали в AX
рис.5
00401D0A 9E sahf // загружаем ah в младший байт регистра eflags
рис.6
00401D0B 7612 jbe $00401d1f // jbe не работает когда 10<100 при флагах CPU: CF=1 и ZF=1
// а теперь вы полнение записи здоровья
00401D0D D905301D4000 fld dword ptr [$00401d30] // грузим 5 в ST(0)
00401D13 D82D98414000 fsubr dword ptr [$00404198] // от здоровья отнимаем ST(0) и сохраняем здоровье в ST(0)
00401D19 D91D98414000 fstp dword ptr [$00404198] // выталкиваем и записываем вновь здоровье
Unit1.cpp.33: updateValue();
00401D1F FF75FC push dword ptr [ebp-$04]
00401D22 E80D000000 call TForm1::updateValue() // ну, а это уже пошла моя функция обновления…
00401D27 59 pop ecx
Заключение.
Короче говоря, берём на зуб:
Unit1.cpp.32: if (fhealth>10) {fhealth-=5;};
00401CF8 D90598414000 fld dword ptr [$00404198]// fhealth
00401CFE D9052C1D4000 fld dword ptr [$00401d2c]// 10.0
00401D04 D9C9 fxch st(1)
00401D06 DAE9 fucompp
00401D08 DFE0 fstsw ax
00401D0A 9E sahf
00401D0B 7612 jbe $00401d1f // if (fhealth>10) {fhealth-=5;};
00401D0D D905301D4000 fld dword ptr [$00401d30]
00401D13 D82D98414000 fsubr dword ptr [$00404198]
00401D19 D91D98414000 fstp dword ptr [$00404198]
Unit1.cpp.33: updateValue();
00401D1F FF75FC push dword ptr [ebp-$04]Этот шаблон вы можете применить в своих скриптах, когда некоторый параметр не должен быть меньше другого некоторого параметра.
Не знаю как вы, я подумал сложные скрипты лучше писать на С++ реализованные в dll, которые можно подгрузить скриптами CE, но об этом позже...
-
Трейнер получился красивым.
Я давным давно склоняюсь к тому чтобы к трейнерам с автоассемблером был некоторый модуль, тогда сами трейнеры могли бы быть малых размеров и они бы работали при условии, что есть этот модуль. А его можно собрать из исходников CheatEngine.
ps Я сделал мини-тутор по работе с FPU числами компилятора Borland C++. Заливаю... Надо ещё понять в какой раздел тему создать с этим тутором.
-
Да, не очень сложен этот FPU. Вообще хорошо бы каждому разобраться с подобными инструкциями окончательно, чтобы как я делать меньше ошибок. Да и мне бы тоже не помешало,поэтому может быть тутор сегодня сделаю, но никому не обещаю.
-
Попробуй исправить на
fld [_m]
fsubp ST,ST(2)
fld [_m]
faddp ST,ST(2)
Где метка _m содержит:
_m:
dd float(20)
В OllyDbg не будут компилироваться float(20) - это естественно.
-
Я написал по памяти, но могу ошибаться. Обычно, ошибки в логике написания инструкций FPU я сразу вижу и испрвляю при пошаговой отладке со справочником перед глазами, что и советую.
[sp=Код][ENABLE]
aobscan(_faddress,f3xxxxxxxx0fxxxxxxxxxxxxxxxx8bxx76xx33xx38xxxxxxxxxx74xxxxxxxxxx)
alloc(_newmem,2048)
label(_returnhere)
label(_originalcode)
label(_newmem2)
label(_flag)
registersymbol(_flag)
_newmem:
pushf
push ecx
mov ecx,[_flag]
cmp al,1
cmp byte ptr [_flag],1
jne _newmem2
cmp dword ptr [eax+30],0
jbe _originalcode
fld [eax+30] // отнимаем 20
fld float(20)
fsubp ST,ST2
fstp [eax+30]
mov byte ptr [_flag],0
jmp _originalcode
_newmem2:
cmp al,2
jne _originalcode
cmp dword ptr [eax+30],42C80000 //100.0
jae _originalcode
fld [ecx+30]
fld float(20)
faddp ST,ST2
fstp [ecx+30]
mov byte ptr [_flag],0
_originalcode:
pop ecx
popf
movss xmm0,[eax+30]
jmp _returnhere
_flag:
dd 0
_faddress: // 006B2965 = JustCause2.exe+2B2965
jmp _newmem
_returnhere:
[DISABLE]
aobscan(_faddress,0fxxxxxxxxxxxxxxxx8bxx76xx33xx38xxxxxxxxxx74xxxxxxxxxx)
_faddress-5:
movss xmm0,[eax+30]
dealloc(_newmem)
//Alt: db F3 0F 10 40 30[/sp]
-
А я знаю почему так
Для этих целей надо пользоваться инструкциями сопроцессора FPU... Так что надо скрипт немного подправить. Если не получится, то я помогу попозже.
-
Windows API (application programming interfaces) — общее наименование целого набора базовых функций интерфейсов программирования приложений операционных систем семейств Windows и Windows NT корпорации «Майкрософт». Является самым прямым способом взаимодействия приложений с Windows. Для создания программ, использующих Windows API, «Майкрософт» выпускает SDK, который называется Platform SDK и содержит документацию, набор библиотек, утилит и других инструментальных средств.
Английская WinApi32: скачать
Русская справка:
Краткий вариант: перейти
Полный вариант: перейти
Ещё справочник перейти
Предназначение функций сразу видно из названия, вам нужно запомнить только для чего какая функция нужна, а далее лазить только по справочникам для уточнения.
Список функций, которые часто используются при создании трейнера:
Менее используемые
Эта функция позволит понять иерархию созданных процессов в том случае если у игры есть запускающий процесс, а надо патчить память дочернего.
Возвратит ошибку той или иной операции
Когда требуется создать поток в чужом процессе выполнив некоторый код. Например, загрузки dll и выполнения её функций внутри чужого процесса. Или выполнение какой-то функции игры по заданным параметрам и т.д.
Загрузка исполняемого модуля в адресное пространство игры.
Хотел также написать функции которые хорошо бы знать “ВООБЩЕ”, а в итоге понял, что надо посоветовать вам читать полный справочник по WinAPI32. Ссылки можете найти в самом верху.
А теперь более подробнее с примерами на языке Дельфи. Принцип работы одинаков на других языках программирования для платформы Windows32/64
Приминение функций:
FindWindow
IsWindow
GetWindowThreadProcessId
OpenProcess
ReadProcessMemory
CloseHandle
Var
hWn: HWND;
PID, hProc, dwReaded,StartAddr,i,r:DWord;
buf: Byte;
begin
hWn := HWND(FindWindow(nil, PChar('Сапер')));
If IsWindow(hWn) Then
Begin
GetWindowThreadProcessId(hWn, PID);
hProc := OpenProcess(PROCESS_VM_READ, False, PID);
Try
If (hProc <> 0) Then
Begin
ReadProcessMemory(hProc, ptr($10056AC), @buf, 1, dwReaded);
SapMap.x := buf;
ReadProcessMemory(hProc, ptr($10056A8), @buf, 1, dwReaded);
SapMap.y := buf;
sg.ColCount := SapMap.x; //sg - таблица
sg.RowCount := SapMap.y;
sg.Height:=16*sg.RowCount+3;
sg.Width:=16*sg.ColCount+3;
form1.Height:=16*sg.RowCount+3+48;
form1.Width:=16*sg.ColCount+9;
For i := 0 To (sg.ColCount - 1) Do
For r := 0 To (sg.RowCount - 1) Do sg.Cells[i,r] := ' ';
StartAddr := $01005361;
For i := 0 To (SapMap.y - 1) Do
Begin
For r := 0 To (SapMap.x - 1) Do
Begin
ReadProcessMemory(hProc, ptr((i*$20) + StartAddr + r), @buf, 1, dwReaded);
If (buf = $8F) Then sg.Cells[r, i] := 'X';
End;
End;
End;
Finally
CloseHandle(hProc);
End;
End;
end;procedure TForm1.Button1Click(Sender: TObject);
Создание трейнера на WinAPI
Создание трейнера на WinAPI. Пример 1
{$R-} {проверка диапазона}
{$S-} {проверка стека}
{$A+} {"выравнивание слов"}
program FlatOut;
uses
windows, messages, commctrl; //Используемые модули,только самое нужное!
var
WinClass : TWndClass; //переменная класса TWndClass для создания главного окна
hInst : HWND; //Хендл приложения
Handle : HWND; //локальный хендл
Com1 : HWND; //TGroupBox
Com2 : HWND; //TButton
Com3 : HWND; //TButton
Com4 : HWND; //TStaticText
Com5 : HWND; //TStaticText
Com6 : HWND; //TStaticText
Msg : TMSG; //сообщения
hFont : HWND; //хендл шрифта
win : hwnd; //хендл данного окна
var
WindowName : integer; //имя окна
ProcessId : integer; //ID процесса
ThreadId : integer; //Поток
buf : PChar;
HandleWindow : Integer; //хендл окна игры
write : cardinal;
const //id наших контролов
id_1 = 1; //TGroupBox
id_2 = 2; //TButton
id_3 = 3; //TButton
id_4 = 4; //TStaticText
id_5 = 5; //TStaticText
id_6 = 6; //TStaticText
const
WindowTitle = 'Flat-Out'; //конец формы, начало формы, точный заголовок игры
Address = $01B40C64; //адрес нашего значения в памяти игры
PokeValue = $FFFFFFFF; //значение на которое мы будем менять
NumberOfBytes = 4; //кол-во байт
{$R XPMan.res} //Здесь у меня лежит иконка моего трейнера и манифес,
//это для того что бы все контролы были в стиле XP
procedure Cheating; //Собственно сама процедура изменения значения
begin
WindowName := FindWindow(nil,WindowTitle); //находим окно игры
If WindowName = 0 then //или,обьясняем пользователю чтобы он запустил игру
MessageBox(win,'Вначале игра,а потом трейнер.','Ошибка',MB_OK or MB_ICONINFORMATION);
ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);
GetMem(buf,1);
buf^ := Chr(PokeValue);
WriteProcessMemory(HandleWindow,ptr(Address),buf,NumberOfBytes,write);//пишем в наш адресс наше значение
FreeMem(buf);
CloseHandle(HandleWindow);//конец формы, началоформы, закрываем хэндл, чтобы не вылететь с ошибкой
end;
procedure ShutDown; //процедура выхода из программы
begin
DeleteObject(hFont); //удаляем шрифт
UnRegisterClass('Sample Class', hInst); //удаляем окно
ExitProcess(hInst); //закрываем окно
Halt; //на всякий случай
end;
procedure About; //наше окно о программе
begin
MessageBox(win, ' [C0DED]: bY g-l-u-k [TeaM - X] ' +#13#10+
' ' +#13#10+
' GreatZzz....: ' +#13#10+
' ' +#13#10+
' Baron_Gede,6aHguT,AllexY ' +#13#10+
' And all TeaM - X Members ! ' +#13#10+
' ' +#13#10+
' Write on pure Delphi (WinAPI) ' +#13#10+
' Сopyright (g-l-u-k)R 2004-2005 ' +#13#10+
' ' +#13#10+
' <!-- m -->[url=http://www.team-x.ru]http://www.team-x.ru[/url]<!-- m --> ' +#13#10+
' e-mail : <!-- e -->[email=g-l-u-k@rambler.ru]g-l-u-k@rambler.ru[/email]<!-- e --> ' +#13#10+
' ' +#13#10+
' GEngine v0.1 ' +#13#10+
' All Right Reserved ',
'About',MB_OK or MB_ICONINFORMATION);
end;
function WindowProc(hwnd, msg, wparam, lparam: longint): longint; stdcall; //обработчик сообщений
begin
Result := DefWindowProc(hwnd, msg, wparam, lparam);
case Msg of
WM_COMMAND:
case LoWord(wParam) of
id_2 :
if HiWord(wParam) = bn_Clicked then
About; //если пользователь нажимает на кнопку "About",получат свой About
id_3 :
if HiWord(wParam) = bn_Clicked then
ShutDown; //если выход то......
end;
WM_DESTROY: ShutDown;
end;
end;
// НАЧАЛО ПРОГРАММЫ
begin
hInst := GetModuleHandle(nil);
with WinClass do
begin
Style := CS_PARENTDC; //стиль класса главного окна
hIcon := LoadIcon(hInstance, IDI_APPLICATION); //иконка программы
lpfnWndProc := @WindowProc; //назначение обработчика сообщений
hInstance := hInst;
hbrBackground := COLOR_BTNFACE + 1; //цвет окна
lpszClassName := 'Sample Class'; //класс окна
hCursor := LoadCursor(0, IDC_ARROW); //активный курсор
end;
InitCommonControls;
RegisterClass(WinClass); //регистрация класса в сис-ме
{Создание главного окна программы}
Handle := CreateWindowEx(0, 'Sample Class', '[FlatOut] Trainer +1',
WS_OVERLAPPED or WS_SYSMENU or
WS_VISIBLE,
503, 345, 234, 222,
0, 0,
hInst, nil);
{Создание шрифта}
hFont := CreateFont(
-12, 0, 0, 0, 0, 0, 0, 0,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH or FF_DONTCARE, 'Terminal');
Com1:=CreateWindow(
'Button',
'Trainer Options:' ,
WS_CHILD or BS_GROUPBOX or WS_VISIBLE,
2,2,222,165, Handle, id_1, hInst,nil);
SendMessage(Com1,WM_SETFONT,hFont,0);
Com2 := CreateWindow(
'Button',
'About',
WS_CHILD or BS_TEXT or WS_VISIBLE,
3, 171, 74, 20,Handle, id_2, hInst, nil);
SendMessage(Com2,WM_SETFONT,hFont,0);
Com3 := CreateWindow(
'Button',
'Quit',
WS_CHILD or BS_TEXT or WS_VISIBLE,
148, 171, 74, 20,Handle, id_3, hInst, nil);
SendMessage(Com3,WM_SETFONT,hFont,0);
Com4 :=CreateWindow(
'Static',
'[F1] :...: More Money' ,
WS_CHILD or SS_LEFT or SS_NOTIFY or WS_VISIBLE,
51,25,117,17,Handle, id_4, hInst,nil);
SendMessage(Com4,WM_SETFONT,hFont,0);
Com5:=CreateWindow(
'Static',
'[C0DED] :...: g-l-u-k [TeaM - X]' ,
WS_CHILD or SS_LEFT or SS_NOTIFY or WS_VISIBLE or WS_DISABLED,
32,145,180,17,Handle, id_5, hInst,nil);
SendMessage(Com5,WM_SETFONT,hFont,0);
Com6:=CreateWindow(
'Static',
'[R.Mouse] :...: About Box ' ,
WS_CHILD or SS_LEFT or SS_NOTIFY or WS_VISIBLE,
26,50,190,17,Handle, id_6, hInst,nil);
SendMessage(Com6,WM_SETFONT,hFont,0);
//Цикл сбора сообщений
while(GetMessage(Msg, Handle, 0, 0)) do
begin
TranslateMessage(Msg); //приём сообщений
if (GetAsyncKeyState(vk_f1 ) <> 0) then Cheating; //если нажата клавиша F1,читим игру
if (GetAsyncKeyState(vk_RButton) <> 0) then About; //если нажата правая кнопка мыши,то показываем About
if (GetAsyncKeyState(vk_Escape ) <> 0) then ShutDown;
DispatchMessage(Msg); //удаление сообщений из очереди
end;
end.//Немного оптимизации
Создание трейнера на WinAPI. Пример 2
…в блокноте файл trainer.rc,который будет содержать следующие строки:
======================trainer.rc============================
100 DIALOG 0, 0, 173, 69
STYLE DS_SETFONT | DS_CENTER | WS_CAPTION | WS_SYSMENU
CAPTION "[FlatOut] Trainer +1"
FONT 8, "Terminal"
{
PUSHBUTTON "About", 102, 2, 52, 36, 15, BS_FLAT
PUSHBUTTON "Close", 101, 132, 52, 38, 15, BS_FLAT
GROUPBOX "Trainer options: ", -1, 1, -1, 172, 51
LTEXT "[C0DED] <--::--> bY g-l-u-k [TeaM - X]", -1, 7, 36, 164, 12, WS_DISABLED
LTEXT "[F1] :.......: More Money", -1, 7, 9, 126, 12, WS_DISABLED
}
======================trainer.rc============================
uses
windows, messages; //Именно по этому наш трейнер должен весить
//меньше,если помните то в прошлой части учебника мы использовали //ещё и commctrl.
const
ID_ABOUT = 102; //Номера контролов нашего ресурса
ID_EXIT = 101;
Elapse = 10; //Нужен для таймера
aboutcap = 'About'; //Наш About Dialog
aboutmsg = ' [C0DED]: bY g-l-u-k [TeaM - X] ' +#13#10+
' ' +#13#10+
' GreatZzz....: ' +#13#10+
' ' +#13#10+
' Baron_Gede,6aHguT,AllexY ' +#13#10+
' And all TeaM - X Members ! ' +#13#10+
' ' +#13#10+
' Write on pure Delphi (WinAPI) ' +#13#10+
' copyright (g-l-u-k)R 2004-2005 ' +#13#10+
' ' +#13#10+
' <!-- m -->[url=http://www.team-x.ru]http://www.team-x.ru[/url]<!-- m --> ' +#13#10+
' e-mail : <!-- e -->[email=g-l-u-k@rambler.ru]g-l-u-k@rambler.ru[/email]<!-- e --> ' +#13#10+
' ' +#13#10+
' GEngine v0.1 ' +#13#10+
' All Right Reserved ';
WindowTitle = 'Flat-Out'; //Название окна игры
Address = $01B40C64;//Адресс нашего значения
PokeValue = $FFFFFFFF;//наше значение
NumberOfBytes = 4;//Кол-во байт
var
Msg : TMSG;
Win : HWND;
WindowName : Integer;
ProcessId : Integer;
ThreadId : Integer;
hInst : Dword;
Buf : PChar;
HandleWindow : Integer;
Write : Cardinal;
{$R trainer.res} //Наш ресурс в котором хранится окно трейнера
//Вот самая интересная часть нашего трейнера,процедура роверки.
//Если TrainerSPY активен,то мы обломим следящего за нашим //трейнером.
function IsTrainerSpyActive:bool;
var
hProcess,hKernel:dword;
addr:pointer;
b:byte;
dummy:cardinal;
proc:pchar;
begin
result:=false;
proc:='WriteProcessMemory';
if FindWindowExA(0,0,nil,'Trainer Spy')<>0 then
begin
result:=true;
exit;
end;
hProcess:=GetCurrentProcess;
hKernel:=LoadLibrary('kernel32.dll');
if hKernel<>0 then
begin
addr:=GetProcAddress(hKernel,proc);
ReadProcessMemory(hProcess,addr,@b,1,dummy);
FreeLibrary(hKernel);
if b=204 then result:=true;
end;
end;
//Процедура взлома игры,описывать полностью не буду,опишу только //самое главное.
procedure Cheating;
begin
if IsTrainerSpyActive then //если TrainerSpy запушен, то вырубаемся
begin
MessageBox(0,'Выруби шпион.','Ошибка зашиты',MB_OK or MB_ICONERROR);
exit;
end;
WindowName := FindWindow(nil,WindowTitle);
If WindowName = 0 then
MessageBox(win,'Игра должна быть запушенна до трейнера','Ошибка',MB_OK or MB_ICONINFORMATION);
ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);
GetMem(buf,1);
buf^ := Chr(PokeValue);
WriteProcessMemory(HandleWindow,ptr(Address),buf,NumberOfBytes,write);
FreeMem(buf);
CloseHandle(HandleWindow);
end;
procedure Quit; //процедура выхода из программы
begin
EndDialog(win,0);
Halt;
end;
procedure DoTimer; //Дополнительна зашита от шпионов
begin
CreateFileA('C:logwmemory.bin',$40000000,1,nil,2,1,0);
hInst:= GetModuleHandle(nil);
SetTimer(hInst,1,Elapse,@DoTimer);
end;
//Обработчик событий нашего окна
function SettingsDlgProc(Window : hWnd; Msg,WParam,LParam : Integer): Integer; StdCall;
begin
case Msg of
wm_InitDialog : begin
end;
wm_Close : DestroyWindow(Win);
wm_Destroy : PostQuitMessage(0);
end;
Result := 0;
case Msg of
WM_COMMAND : begin
if wParam = ID_EXIT then Quit; //Ели нажата кнопка выхода,выходим
if wParam = ID_ABOUT then MessageBox(Win,aboutmsg,aboutcap,MB_OK or MB_ICONINFORMATION);//А это наш //about
end;
end;
end;
//Процедура создания главного окна
Procedure RunSettings;
begin
Win := CreateDialog(hInstance,PCHar(100),0,@SettingsDlgProc);
Showwindow(Win,SW_SHOW);
Updatewindow(Win);
end;
// Начало программы
begin
RunSettings;
while GetMessage(Msg,0,0,0) do
begin
//Цикл сбора сообщений
TranslateMessage(Msg);
if (GetAsyncKeyState(VK_F1) <> 0) then Cheating;//Горячая клавиша
DispatchMessage(Msg);
end;
end.program FlatOut; //Опять будем ломать FlatOut
Заключение.
Можно было заметить как был построен материал. Сначала была дана справочная информация, а затем можно было увидеть применение функций WinApi в коде трейнеров. Как вы понимаете это базовая основа, т.к. некоторые тонкости не освещены. Например, поиск процесса игры по имени процесса, а не по имени окна. Здесь нет функций сканирования сигнатур для поиска адреса внедрения. Нет проверки на повторный запуск трейнера. Нет отмены читов при закрытии трейнера… Читы не выделяются в выделенную память. Байт-код внедряемых игровых инструкций пишется в память игры через байты, которые нужно получать предварительно при компиляции. Я бы эти байты не получал бы таким образом, а получил бы их скомпилировав инструкции в самом трейнере, а затем бы их копировал бы в игровой процесс в выделенную память. Возможно, я ещё что-то забыл указать…
-
Я поправил исходники. До этого не хватало описания структуры "TPole" и общего описания модуля, теперь оно полное.
-
Ну, я так понял что проблема уже решена.
-
Я согласен с тем, что подбор проверочных байт может быть не уникальным я это замечал несколько раз. Пользователю придётся каждый раз при создании скрипта проверять их уникальность самостоятельно. Пока их генерацию в таком виде в каком она сейчас существует это лучшее что я могу предложить.
-
Игру я вроде запускал через D3DWindower-English.exe
Я плохо понял термин "завесить"
-
Ссылка (нажать на синюю стрелку для закачки)
-
В общем Xipho помог мне с инсталлером и он сделал поиск пути по ключу в реестре
[sp=Ключ][HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionUninstallCheat Engine 5.6_is1]
"Inno Setup: Setup Version"="5.3.6 (a)"
"Inno Setup: App Path"="E:ReversingGamehackИнструментыCheat Engine5.6"
"InstallLocation"="E:ReversingGamehackИнструментыCheat Engine5.6"
"Inno Setup: Icon Group"="Cheat Engine 5.6"
"Inno Setup: No Icons"=dword:00000001
"Inno Setup: User"="_"
"Inno Setup: Selected Tasks"=""
"Inno Setup: Deselected Tasks"="desktopicon"
"DisplayName"="Cheat Engine 5.6"
"UninstallString"=""E:ReversingGamehackИнструментыCheat Engine5.6unins000.exe""
"QuietUninstallString"=""E:ReversingGamehackИнструментыCheat Engine5.6unins000.exe" /SILENT"
"Publisher"="Dark Byte"
"URLInfoAbout"="http://www.cheatengine.org/"
"HelpLink"="http://www.cheatengine.org/"
"URLUpdateInfo"="http://www.cheatengine.org/"
"NoModify"=dword:00000001
"NoRepair"=dword:00000001
"InstallDate"="20100221"[/sp]
Что-то я не понял. Инсталлер не правильно дописывает путь с существующей оригинальной версией? Подразумевается что у пользователя одна версия оригинальной CE и одна папка
Делать две отдельных папки для CE RUS и оригинальной думаю не стоит ) всё предпочтительно в одной. Ну а если кто хочет две разные папки, то придётся в ручную делать.
Cheat Engine. Общие вопросы по работе с программой
in Вопросы по утилитам
Опубликовано
Если нужны мои комментарии, то первый твой пример в новинку может быть полезным для многих, а второй можно воспринять как закрепление материала по "активирующим" скриптам![:)](https://gamehacklab.ru/uploads/emoticons/default_smile.png)
Немного поправлю:
newmem:
cmp byte ptr [_Exp],1
jne short originalcode // корткий прыжок не пишет лишних нопов в теле чит-кода
add [ecx+60],3E8
mov [_Exp],0
originalcode:
mov eax,[ecx+60]
ret // эта инструкция "конечная станция"
поэтому...
int 3// ...можно было не писать
jmp returnhere // ...можно было не писать