
EPIC送了GTA5,最近考完試和朋友一起玩了下,由于剛開始,等級低,朋友帶我打任務(wù)的時(shí)候,完全就打不過,我的甲沒幾下就爆了,剛開始沒什么。
但是今天去打末日3的時(shí)候,由于只有兩個(gè)人,玩過的都知道,末日3是需要一個(gè)人去玩小游戲,然后其他人保護(hù)玩小游戲那個(gè)隊(duì)友,末日3的NPC是會(huì)一直刷,怎么打都打不完,有一瞬間就變成了CSGO練爆頭模式,完全沒得打(打不過就充錢,沒得充錢就變強(qiáng))。后面搜了搜,發(fā)現(xiàn)別人歪瓜的截圖,有鎖血功能,就想去了解一下。
打開GTAV,進(jìn)入故事模式。GTAV官方在故事模式中,有給出作弊代碼,其中有一個(gè)無敵五分鐘的命令。從這個(gè)命令入手,查找他的基址,沒準(zhǔn)能用。
作弊代碼:PAINKILLER
搜索思路
??
開啟無敵時(shí)候,可能會(huì)有數(shù)值增加,可能有一個(gè)數(shù)值或者開關(guān),會(huì)變?yōu)?。這時(shí)搜索增加的數(shù)值
關(guān)閉無敵時(shí)候,可能會(huì)有數(shù)值減少,可能有一個(gè)數(shù)值或者開關(guān),會(huì)變?yōu)?。這時(shí)搜索減少的數(shù)值
開啟無敵時(shí)候,可能會(huì)有數(shù)值減少,可能有一個(gè)數(shù)值或者開關(guān),某個(gè)數(shù)值減少。這時(shí)搜索減少的數(shù)值
關(guān)閉無敵時(shí)候,可能會(huì)有數(shù)值減少,可能有一個(gè)數(shù)值或者開關(guān),某個(gè)數(shù)值增加。這時(shí)搜索增加的數(shù)值
??
搜索基址

CE加載GTA5,先通過常規(guī)思路,無敵搜增加,關(guān)閉搜減少。

即使這游戲是64位的,但是依然掃4字節(jié)就夠了。首先先掃一次未知的初始值。

進(jìn)入GTA,按~鍵,輸入作弊代碼PAINKILLER

輸入成功時(shí)候,右下角會(huì)有無敵時(shí)間,正常為五分鐘。

這時(shí)候去CE搜索增加的數(shù)值



像我這樣,C盤直接沒位置了,所以建議備一個(gè)清理C盤的軟件。時(shí)不時(shí)清理臨時(shí)文件
看會(huì)小說或者刷會(huì)短視頻,等掃描結(jié)果出來以后,就再次輸入作弊代碼,就可以將無敵關(guān)閉(我一開始不知道,傻傻得等了五分鐘)。

如何查看是否關(guān)閉,看右下角的倒計(jì)時(shí)是否存在。如果沒了,那就是關(guān)閉了。
這時(shí)候搜索減少的數(shù)值。

步驟循環(huán),等結(jié)果少于5000的時(shí)候。就可以通過搜索未變動(dòng)的數(shù)值,來開始查找最終的基址。



快捷鍵,CTRL+D,可以快速查看該地址反匯編地址。
因?yàn)槲宜阉鬟^很多次,所以認(rèn)得特征,當(dāng)反匯編窗口的代碼,是以下這串時(shí)候,我就知道找到了
or [rax],al

為什么了,因?yàn)檫@個(gè)al在線上模式時(shí)候,他是0或者1,0就是關(guān)閉無敵,1就是開啟無敵。
一開始我怎么找到這個(gè)的了,我當(dāng)時(shí)掃了很多次,發(fā)現(xiàn)有一次,他的值不是al,而是0,然后當(dāng)我開啟無敵時(shí)候,他就變?yōu)?了。我就把它認(rèn)住了。發(fā)現(xiàn)有的時(shí)候在線下模式,他就是會(huì)這樣,抽風(fēng)。
關(guān)閉無敵的默認(rèn)初始值是:4194316
當(dāng)你把他的數(shù)值改為這個(gè)初始值時(shí),他這個(gè)al就會(huì)變?yōu)?

但是即使你把它改了,他還是會(huì)變,有時(shí)候會(huì)變?yōu)?code>12,有時(shí)候會(huì)變很大的數(shù),但是你測試一下,將00改為01時(shí)候,在去做一些扣血的行為時(shí)候,是不會(huì)扣血的,達(dá)到了無敵的效果。
基址找到了,那就開始找他的靜態(tài)基址。
一開始嘗試過一步一步找,然后發(fā)現(xiàn),掃出來的基址過多,比較麻煩,所以后面決定使用指針?biāo)阉?/p>

我這里沒調(diào)參數(shù),直接確定了。

最后掃出來的結(jié)果,有9419539個(gè),觀察他的值,值等于初始值4194316或等于基址目前的值的時(shí)候,就把它雙擊,添加到地址表中。


一定要選多幾個(gè),因?yàn)檫M(jìn)入線上模式時(shí)候,值會(huì)變,有的會(huì)變成?號,有的是不知道啥值,所以要選多幾個(gè)加以觀察,或者不關(guān)閉指針掃描結(jié)果窗口。

最終我找出來的基址
"GTA5.exe"+02D06110 +28+298+c8+a8+188五層偏移。

代碼編寫

游戲是64位的,所以要用64位的函數(shù)。
WOW64 函數(shù)
ZwWow64QueryInformationProcess64
ZwWow64ReadVirtualMemory64
ZwWow64WriteVirtualMemory64
ZwWow64CallFunction64
32位程序幾乎可以獲取64位程序的全部函數(shù)調(diào)用功能。
首先導(dǎo)入函數(shù)
基本操作都沒啥不同的。
# -*- coding:utf-8 -*-
"""
@author:
@file: GTA.py
@time: 2020-06-21 13:49
@desc: KeyboArd
"""
import win32process#進(jìn)程模塊
import win32api#主要用來注冊熱鍵
import ctypes#C語言類型
from win32gui import FindWindow#界面
from ctypes import c_long , c_int , c_void_p, windll, WinDLL, c_ulonglong, byref
初始化
ntdll = WinDLL("ntdll.dll")#調(diào)用ntdll庫
kernel32 = ctypes.windll.LoadLibrary("kernel32.dll")#調(diào)用kernel32庫
GetLastError = kernel32.GetLastError#調(diào)用GetLastError函數(shù)
GTA = 0x7FF79B5D0000#GTA.EXE的基址,原本是打算通過模塊名字獲取的,但是不知道為什么返回-1.可能是64位游戲,涉及到一些權(quán)限問題,后面在研究一下,目前主要是為了達(dá)到目的。
STANDARD_RIGHTS_REQUIRED = 0x000F0000#這兩個(gè)的值,是在C++那里獲取過來的。
SYNCHRONIZE = 0x00100000#看圖
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)#看圖
class PROCESS_BASIC_INFORMATION(ctypes.Structure):
_fields_ = [('ExitStatus', ctypes.c_ulonglong), # 接收進(jìn)程終止?fàn)顟B(tài)
('PebBaseAddress', ctypes.c_ulonglong), # 接收進(jìn)程環(huán)境塊地址
('AffinityMask', ctypes.c_ulonglong), # 接收進(jìn)程關(guān)聯(lián)掩碼
('BasePriority', ctypes.c_ulonglong), # 接收進(jìn)程的優(yōu)先級類
('UniqueProcessId', ctypes.c_ulonglong), # 接收進(jìn)程ID
('InheritedFromUniqueProcessId', ctypes.c_ulonglong)] # 接收父進(jìn)程ID
## OpenProcess
OpenProcess = windll.kernel32.OpenProcess
OpenProcess.argtypes = [c_void_p, c_int, c_long]
OpenProcess.rettype = c_long
## CloseHandle
CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = [c_void_p]
CloseHandle.rettype = c_int
def _GetProcessId(className,windowName):
hGameWindow = FindWindow(className, windowName)
pid = win32process.GetWindowThreadProcessId(hGameWindow)[1]
return pid
def _GetPorcessHandle(pid):
hGameHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
return hGameHandle
ProcessId = _GetProcessId("grcWindow", u"Grand Theft Auto V")
_hGameHandle = _GetPorcessHandle(ProcessId)


對64位內(nèi)存讀操作
def _ReadMemeryLong64(addr, bufflength):
addr = c_ulonglong(addr)#64位游戲,我測試過,如果只寫c_ulong() 會(huì)報(bào)錯(cuò),說有沖突。
ret = c_ulonglong()#所以這里也都保持同樣的長度把
BufferLength = c_ulonglong(bufflength)
ntdll.ZwWow64ReadVirtualMemory64(int(_hGameHandle), addr, byref(ret), BufferLength, 0)#ZwWow64ReadVirtualMemory64和ReadProcessInt 這個(gè)函數(shù)操作類似。
return ret.value
64位通過模塊名獲取基址
def GetBaseAddr(ModuleName): #這是論壇里一位大佬寫的,為了方便快捷,先復(fù)制粘貼過來,這代碼我也沒看懂,好像涉及到了pe知識,后面自己在 寫一個(gè)。
# 傳入需要查找的模塊的名稱,就可以返回相應(yīng)的模塊基址了
NumberOfBytesRead = c_ulong()
Buffer = PROCESS_BASIC_INFORMATION()
Size = c_ulong(48)
name_len = len(ModuleName)
ntdll.NtWow64QueryInformationProcess64(int(_hGameHandle), 0, byref(Buffer), Size,
byref(NumberOfBytesRead))
"""
這同樣是一個(gè)未公開的api,可以通過他獲取進(jìn)程的信息,然后存入我們一開始定義的結(jié)構(gòu)體中,他的五個(gè)參數(shù)分別是:
進(jìn)程句柄,信息類型,緩沖指針,以字節(jié)為單位的緩沖大小, 寫入緩沖的字節(jié)數(shù)
而至于下面為什么要這么寫,其實(shí)涉及到了程序的PE結(jié)構(gòu),這里不做贅述,因?yàn)檫@個(gè)東西不是一會(huì)會(huì)說的清楚的,可以自行百度
"""
ret = _ReadMemeryLong64(Buffer.PebBaseAddress + 24, 8)
ret = _ReadMemeryLong64(ret + 24, 8)
for i in range(100000): # 這里用for循環(huán)其實(shí)是怕程序卡死,下面如果出了問題不能退出的話,循環(huán)結(jié)束一樣可以退出
modulehandle = _ReadMemeryLong64(ret + 48, 8)
if modulehandle == 0:
break
nameaddr = _ReadMemeryLong64(ret + 96, 8)
name = ReadProcessMemory64_Wchar(nameaddr, name_len * 2 + 1, name_len)
if name == ModuleName:
return modulehandle
ret = _ReadMemeryLong64(ret + 8, 8)
對找到的基址進(jìn)行讀取
def get_invincible(hGameHandle):
#"GTA5.exe"+02D06110 +28+298+c8+a8+188 這是找到的基址
value = GTA+ 0x02D06110#GTA的基址等于0x7FF79B5D0000 ,如果重啟了電腦,這個(gè)基址會(huì)變,所以一定要寫64位通過模塊名讀取基址。
value1 = _ReadMemeryLong64(value,8)#先讀取一下"GTA5.exe"+02D06110的基址。看下是否正確。
value2 = _ReadMemeryLong64(value1+0x28,8)
value3 = _ReadMemeryLong64(value2+0x298,8)
value4 = _ReadMemeryLong64(value3+0xc8,8)
value5 = _ReadMemeryLong64(value4+0xa8,8)
value6 = _ReadMemeryLong64(value5+0x188,8)
# #value1 = value++0x28+0x298+0xc8+0xa8+0x188
print("0x%08X"%value2)
print(value)
return value5
value:"GTA5.exe"+02D06110

value1:GTA5.exe"+02D06110

value2:GTA5.exe"+02D06110+0x28

value3:GTA5.exe"+02D06110+0x28+0x298

value4:GTA5.exe"+02D06110+0x28+0x298+0XC8

value5:GTA5.exe"+02D06110+0x28+0x298+0XC8+0xa8

value6:GTA5.exe"+02D06110+0x28+0x298+0XC8+0xa8+0x188

成功讀取到該值,為什么這么寫,因?yàn)橐婚_始我直接省略麻煩,寫一堆,發(fā)現(xiàn)讀的都是什么東西,太亂了。這樣子容易找到問題。
64位寫操作
def WriteMemeryLong64(addr, bufflength, n):
addr = c_ulonglong(addr)
ret = c_ulonglong(bufflength)
BufferLength = c_ulonglong(n)
ntdll.ZwWow64WriteVirtualMemory64(int(_hGameHandle), addr, byref(ret), BufferLength, 0)##寫法也跟WriteProcessMemory這個(gè)函數(shù)相似
無敵
初始:4194316
無敵:4194572
def set_invincible(hGameHandle,address):
while True:
WriteMemeryLong64(hGameHandle,address+0x188,4194572,4)#開啟無敵
if(win32api.GetAsyncKeyState(35)!=0):#當(dāng)按下END鍵,就關(guān)閉無敵,將值寫回正常
WriteMemeryLong64(hGameHandle, address + 0x188, 4194316, 4)#關(guān)閉無敵
CloseHandle(hGameHandle)#關(guān)閉句柄
exit(0)#退出程序
開啟:

關(guān)閉:

完整代碼
# -*- coding:utf-8 -*-
"""
@author:
@file: GTA.py
@time: 2020-06-21 13:49
@desc: KeyboArd
"""
import win32process#進(jìn)程模塊
import win32api#主要用來注冊熱鍵
import ctypes#C語言類型
from win32gui import FindWindow#界面
from ctypes import c_long , c_int , c_void_p, windll, WinDLL, c_ulonglong, byref,Structure,c_char,POINTER,sizeof,pointer,c_ulong,c_wchar_p
ntdll = WinDLL("ntdll.dll")
kernel32 = ctypes.windll.LoadLibrary("kernel32.dll")
GetLastError = kernel32.GetLastError
#GTA = 0x7FF79B5D0000
STANDARD_RIGHTS_REQUIRED = 0x000F0000
SYNCHRONIZE = 0x00100000
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)
class PROCESS_BASIC_INFORMATION(ctypes.Structure):
_fields_ = [('ExitStatus', ctypes.c_ulonglong), # 接收進(jìn)程終止?fàn)顟B(tài)
('PebBaseAddress', ctypes.c_ulonglong), # 接收進(jìn)程環(huán)境塊地址
('AffinityMask', ctypes.c_ulonglong), # 接收進(jìn)程關(guān)聯(lián)掩碼
('BasePriority', ctypes.c_ulonglong), # 接收進(jìn)程的優(yōu)先級類
('UniqueProcessId', ctypes.c_ulonglong), # 接收進(jìn)程ID
('InheritedFromUniqueProcessId', ctypes.c_ulonglong)] # 接收父進(jìn)程ID
## OpenProcess
OpenProcess = windll.kernel32.OpenProcess
OpenProcess.argtypes = [c_void_p, c_int, c_long]
OpenProcess.rettype = c_long
## CloseHandle
CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = [c_void_p]
CloseHandle.rettype = c_int
def _GetProcessId(className,windowName):
hGameWindow = FindWindow(className, windowName)
pid = win32process.GetWindowThreadProcessId(hGameWindow)[1]
return pid
def _GetPorcessHandle(pid):
hGameHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
return hGameHandle
ProcessId = _GetProcessId("grcWindow", u"Grand Theft Auto V")
_hGameHandle = _GetPorcessHandle(ProcessId)
def _ReadMemeryLong64(addr, bufflength):
addr = c_ulonglong(addr)
ret = c_ulonglong()
BufferLength = c_ulonglong(bufflength)
ntdll.ZwWow64ReadVirtualMemory64(int(_hGameHandle), addr, byref(ret), BufferLength, 0)
return ret.value
def WriteMemeryLong64(addr, bufflength, n): #
addr = c_ulonglong(addr)
ret = c_ulonglong(bufflength)
BufferLength = c_ulonglong(n)
ntdll.ZwWow64WriteVirtualMemory64(int(_hGameHandle), addr, byref(ret), BufferLength, 0)
def ReadProcessMemory64_Wchar(addr, n,length):#這個(gè)函數(shù)用以讀取模塊名稱,與ReadVirtualMemory64不同的點(diǎn)還有一個(gè)是我們會(huì)傳入一個(gè)緩沖區(qū)的長度length,用于定義一個(gè)c_wchar_p的緩沖區(qū)
addr = c_ulonglong(addr)
ret = c_wchar_p("0" * length)#這里選用wchar其實(shí)與編碼有關(guān),感興趣的同學(xué)自行百度wchar,寬字符等關(guān)鍵字學(xué)習(xí)
BufferLength = c_ulonglong(n)
ntdll.NtWow64ReadVirtualMemory64(int(_hGameHandle), addr, ret, BufferLength, 0)
return ret.value
def GetBaseAddr(ModuleName): #
# 傳入需要查找的模塊的名稱,就可以返回相應(yīng)的模塊基址了
NumberOfBytesRead = c_ulong()
Buffer = PROCESS_BASIC_INFORMATION()
Size = c_ulong(48)
name_len = len(ModuleName)
ntdll.NtWow64QueryInformationProcess64(int(_hGameHandle), 0, byref(Buffer), Size,
byref(NumberOfBytesRead))
"""
這同樣是一個(gè)未公開的api,可以通過他獲取進(jìn)程的信息,然后存入我們一開始定義的結(jié)構(gòu)體中,他的五個(gè)參數(shù)分別是:
進(jìn)程句柄,信息類型,緩沖指針,以字節(jié)為單位的緩沖大小, 寫入緩沖的字節(jié)數(shù)
而至于下面為什么要這么寫,其實(shí)涉及到了程序的PE結(jié)構(gòu),這里不做贅述,因?yàn)檫@個(gè)東西不是一會(huì)會(huì)說的清楚的,可以自行百度
"""
ret = _ReadMemeryLong64(Buffer.PebBaseAddress + 24, 8)
ret = _ReadMemeryLong64(ret + 24, 8)
for i in range(100000): # 這里用for循環(huán)其實(shí)是怕程序卡死,下面如果出了問題不能退出的話,循環(huán)結(jié)束一樣可以退出
modulehandle = _ReadMemeryLong64(ret + 48, 8)
if modulehandle == 0:
break
nameaddr = _ReadMemeryLong64(ret + 96, 8)
name = ReadProcessMemory64_Wchar(nameaddr, name_len * 2 + 1, name_len)
if name == ModuleName:
return modulehandle
ret = _ReadMemeryLong64(ret + 8, 8)
def get_invincible(hGameHandle,GTA):
#"GTA5.exe"+02D06110 +28+298+c8+a8+188
value = GTA+ 0x02D06110
value1 = _ReadMemeryLong64(value,8)#先讀取一下"GTA5.exe"+02D06110的基址??聪率欠裾_。
value2 = _ReadMemeryLong64(value1+0x28,8)
value3 = _ReadMemeryLong64(value2+0x298,8)
value4 = _ReadMemeryLong64(value3+0xc8,8)
value5 = _ReadMemeryLong64(value4+0xa8,8)
value6 = _ReadMemeryLong64(value5+0x188,8)
#value1 = value++0x28+0x298+0xc8+0xa8+0x188
# print("0x%08X"%value6)
print(value6)
return value5
def set_invincible(hGameHandle,address):
while True:
WriteMemeryLong64(address+0x188,4194572,4)#開啟無敵
if(win32api.GetAsyncKeyState(35)!=0):
WriteMemeryLong64(address + 0x188, 4194316, 4)#關(guān)閉無敵
CloseHandle(hGameHandle)
exit(0)
def main():
moudle = GetBaseAddr("GTA5.exe")
invincible = get_invincible(_hGameHandle, moudle)
#print("0x%08X" % moudle)
#print(moudle)
#print("0x%08X" % modulename)
set_invincible(_hGameHandle,invincible)
CloseHandle(_hGameHandle)
if __name__ == '__main__':
main()


