Каждый геймер рано или поздно задумывается над упрощением прохождения некоторых уровней игры, возможности сжульничать и т. д. Для этого прибегают к специальным программам, типа ArtMoney, но это не всегда возможно и порой бывает утомительно периодически подправлять данные в памяти для достижения поставленных целей. Автоматизировать данный процесс помогают различные читы и трейнеры. О создании чита далее пойдет речь.
В качестве подопытной была выбрана игра GTA San Andreas и ее будем мучить. Для того, чтобы «подкручивать» игровую картину как нам надо, достаточно изменять некоторые области памяти. Как это сделать? Ведь в Windows процессы изолированы друг от друга и просто так не залезть в память другого процесса. Конечно можно использовать WinAPI функцию WriteProcessMemory(), но мы поступим по другому, добавив свой код в процесс игры. Есть несколько способов это сделать, в т. ч. инжект в требуемый процесс, но этот метод «не любят» антивирусы и могут поднять тревогу. Поэтому используем свойство винды искать библиотеки сначала в папке с запущенной программой, а затем уже в других местах. Этот метод как правило у антивирусов не вызывает подозрений.
Нужно посмотреть какие функции из каких библиотек импортируются программой, найти ту, из которой импортируется меньше всего функций и написать для нее DLL-фильтр. В случае GTA San Andreas, такой библиотекой оказалась Dinput8.dll, из которой импортируется всего одна функция — DirectInput8Create().
Теперь нужно создать свою DLL, в которой кроме всего прочего обязательно должна экспортироваться функция DirectInput8Create() при вызове которой, должна вызываться аналогичная из одноименной системной библиотеки WWindows. Иначе нарушится работа игры!
Создать DLL можно практически на любом языке программирования. Я выбрал PureBasic.
; Чит для GTA-San Andreas.
Prototype pDirectInput8Create(hinst, dwVersion.l, riidltf, *ppvOut, *punkOuter)
; Системная папка.
Procedure.s GetSysDir()
Protected Result.s="", len, *Mem
*Mem=AllocateMemory(#MAX_PATH)
If *Mem
Len=GetSystemDirectory_(*Mem, #MAX_PATH)
Result=PeekS(*Mem, Len)+""
FreeMemory(*Mem)
EndIf
ProcedureReturn Result
EndProcedure
Procedure.b TestMemory(*Pointer, Size) ; Проверка доступности для процесса указаного участка памяти.
Protected mbi.MEMORY_BASIC_INFORMATION
Protected Result.b = #False, dwWrote
If Size
dwWrote = VirtualQuery_(*Pointer, @mbi, SizeOf(MEMORY_BASIC_INFORMATION))
If dwWrote
If mbiBaseAddress+mbiRegionSize >= *Pointer+Size
If mbiProtect & (#PAGE_READONLY | #PAGE_READWRITE | #PAGE_EXECUTE_READ | #PAGE_EXECUTE_READWRITE)
Result = #True
EndIf
EndIf
EndIf
EndIf
ProcedureReturn Result
EndProcedure
Procedure WriteMemF(*Address, Infa.f) ; Запись в память Float числа.
If TestMemory(*Address, 4)
PokeF(*Address, Infa)
EndIf
EndProcedure
Procedure WriteMemL(*Address, Infa.l) ; Запись в память Long числа.
If TestMemory(*Address, 4)
PokeL(*Address, Infa)
EndIf
EndProcedure
Procedure.l ReadMemL(*Address) ; Чтение из памяти Long числа.
If TestMemory(*Address, 4)
ProcedureReturn PeekL(*Address)
Else
ProcedureReturn -1
EndIf
EndProcedure
Procedure WinTimer()
Protected *Point
Static Count
; Деньги (если меньше 1000000, то добавятся).
Maney = ReadMemL($00B7CE50)
If Maney>=0 And Maney<1000000
WriteMemL($00B7CE50, 1000000)
EndIf
; Игрок и оружие.
If TestMemory($00B6F5F0, SizeOf(Integer))
*Point = PeekI($00B6F5F0)
If TestMemory(*Point, 4)
; Здоровье.
WriteMemF(*Point+1344, 100)
; Броня.
WriteMemF(*Point+1352, 100)
; Пистолеты, патроны в обойме.
WriteMemL(*Point+1504, 1000)
WriteMemL(*Point+1508, 1000)
; Дробовики, патроны в обойме.
WriteMemL(*Point+1532, 1000)
WriteMemL(*Point+1536, 1000)
; SMG, патроны в обойме.
WriteMemL(*Point+1560, 1000)
WriteMemL(*Point+1564, 1000)
; Автоматы, патроны в обойме.
WriteMemL(*Point+1588, 1000)
WriteMemL(*Point+1592, 1000)
; Винтовки, патроны в обойме.
WriteMemL(*Point+1616, 1000)
WriteMemL(*Point+1620, 1000)
; Тяжелое, патроны в обойме.
WriteMemL(*Point+1644, 1000)
WriteMemL(*Point+1648, 1000)
; Метательное, патроны в обойме.
WriteMemL(*Point+1672, 1000)
WriteMemL(*Point+1676, 1000)
; Огнетушитель/Фотокамера, патроны в обойме.
WriteMemL(*Point+1700, 1000)
WriteMemL(*Point+1704, 1000)
EndIf
EndIf
; Транспорт.
If TestMemory($00BA18FC, SizeOf(Integer))
*Point = PeekI($00BA18FC)
If TestMemory(*Point, 4)
; Здоровье транспортного средства.
WriteMemF(*Point+1216, 1000)
EndIf
EndIf
If Count>10
Count=0
; Игрок - Навык мотоцикла (макс 1000).
WriteMemL($00B791B4, 1000)
; Игрок - Навык велосипеда (макс 1000).
WriteMemL($00B791B8, 1000)
; Игрок - Навык вождения (макс 1000).
WriteMemL($00B790A0, 1000)
; Игрок - Навык полета (макс 1000).
WriteMemL($00B7919C, 1000)
; Игрок - Удача (макс 1000).
WriteMemL($00B791C4, 1000)
; Игрок - Объем легких (макс 1000).
WriteMemL($00B791A4, 1000)
; Игрок - Мускулатура (макс 1000).
WriteMemF($00B793DC, 1000)
; Игрок - Уважение (макс 1000).
;WriteMemF($00B79480, 1000)
; Игрок - Выносливость (макс 1000).
WriteMemF($00B793D8, 1000)
; Оружие - Навык AK47 (макс 1000).
WriteMemF($00B794B4, 1000)
; Оружие - Навык боевого дробовика (макс 1000).
WriteMemF($00B794A8, 1000)
; Оружие - Навык Desert Eagle (макс 1000).
WriteMemF($00B7949C, 1000)
; Оружие - Навык M4 (макс 1000).
WriteMemF($00B794B8, 1000)
; Оружие - Навык пистолета-автомата (макс 1000).
WriteMemF($00B794AC, 1000)
; Оружие - Навык пистолета (макс 1000).
WriteMemF($00B79494, 1000)
; Оружие - Навык SMG (макс 1000).
WriteMemF($00B794B0, 1000)
; Оружие - Навык обреза (макс 1000).
WriteMemF($00B794A4, 1000)
; Оружие - Навык дробовика (макс 1000).
WriteMemF($00B794A0, 1000)
; Оружие - Навык пистолета с глушителем (макс 1000).
WriteMemF($00B79498, 1000)
Else
Count+1
EndIf
EndProcedure
; Код процедуры работает в параллельном потоке.
Procedure Thread(*Void)
Shared hWnd_SanAndreas
Protected Count
Delay(4000)
Count=0
Repeat
; Ищем окно игры.
hWnd_SanAndreas=FindWindow_(0, "GTA: San Andreas")
If hWnd_SanAndreas<>0 ; Если окно найдено
SetTimer_(hWnd_SanAndreas, 10, 800, @WinTimer()) ; то запускаем таймер
Break ; и прерываем цикл.
EndIf
Delay(100)
Count+1
If Count>20 ; Окно не найдено.
Beep_(1000,200)
Break
EndIf
ForEver
EndProcedure
; Процедура вызывается системой при загрузке DLL.
ProcedureDLL AttachProcess(Instance)
Global WinAPI_DirectInput8Create=0, hLib_Dinput8, ThreadID
; Подгружаем библиотеку Dinput8.dll
hLib_Dinput8 = LoadLibrary_(GetSysDir()+"Dinput8.dll")
If hLib_Dinput8
WinAPI_DirectInput8Create.pDirectInput8Create = GetProcAddress_(hLib_Dinput8, "DirectInput8Create")
EndIf
If WinAPI_DirectInput8Create=0
MessageRequester("Чит", "Не удалось получить указатель."+Chr(10)+"Перезапустите игру.")
EndIf
; Создаем параллельный поток.
ThreadID=CreateThread(@Thread(), 0)
EndProcedure
; Процедура вызывается системой при выгрузке DLL.
ProcedureDLL DetachProcess(Instance)
Shared hWnd_SanAndreas
If hLib_Dinput8
FreeLibrary_(hLib_Dinput8)
hLib_Dinput8 = 0
EndIf
If ThreadID And IsThread(ThreadID)
KillThread(ThreadID)
ThreadID=0
EndIf
If hWnd_SanAndreas<>0 And IsWindow_(hWnd_SanAndreas)
KillTimer_(hWnd_SanAndreas, 10)
hWnd_SanAndreas=0
EndIf
EndProcedure
; Собственно вызов системной функции WinAPI DirectInput8Create.
ProcedureDLL DirectInput8Create(hinst, dwVersion.l, riidltf, *ppvOut, *punkOuter)
If WinAPI_DirectInput8Create
ProcedureReturn WinAPI_DirectInput8Create(hinst, dwVersion, riidltf, *ppvOut, *punkOuter)
EndIf
EndProcedure
Процедура AttachProcess() вызывается системой при загрузке DLL. В ней подгружается системная библиотека Dinput8.dll. При этом явно указан абсолютный путь загрузки, иначе библиотека загружала бы сама себя, а нам это не надо! При успешной загрузке библиотеки, получаем указатель на ее функцию DirectInput8Create(). В случае неудачи, выводится мессага, сообщающая юзеру о сбое. После этого запускается параллельный поток и работа процедуры завершается. Поток нам нужен чтобы не «вешать» программу. В потоке (процедура Thread()) производится поиск окна игры, по заголовку «GTA: San Andreas». Как только окно найдено, на него «навешивается» таймер и на этом работа потока прекращается. Конечно можно было бы обойтись без таймера и все действия выполнять в потоке, но в целях безопасности, было принято решение, модифицировать память из основного потока игры.
Процедура WinTimer() вызывается по таймеру каждые 800 миллисекунд. В ней изменяются значения требуемых переменных. Но перед изменением, обязательно проверяется доступен ли этот адрес процессу или нет.
Чит поддерживает на должном уровне броню, здоровье игрока и транспортного средства, навыки игрока, а так же количество патронов. Кроме того, игроку выдается лимон баксов и если он их тратит, по они вновь появляются из ничего.
Данный чит понравится тем, кто любит развязывать войнушку с копами. Им будет очень сложно уничтожить игрока даже стреляя в него в упор.
Из этого кода нужно создать DLL с именем Dinput8.dll и поместить ее в одну папку с игрой.
Чтобы получилась именно DLL, нужно выбрать в свойствах компилятора в списке «Формат исполняемого файла», пункт «Shared Dll» при загруженном кода в IDE.
Автор: hachik
как перейти в свойства компилятора? в интернете ничего не нашёл помогите плиз.