Pull to refresh

Пишем чит для GTA San Andreas

Reading time 6 min
Views 30K
Каждый геймер рано или поздно задумывается над упрощением прохождения некоторых уровней игры, возможности сжульничать и т. д. Для этого прибегают к специальным программам, типа 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 mbi\BaseAddress+mbi\RegionSize >= *Pointer+Size
        If mbi\Protect & (#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.

image
Tags:
Hubs:
+11
Comments 38
Comments Comments 38

Articles