使用Python讀寫游戲(GTA)

image

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ù)值
??

搜索基址

image

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

image

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

image

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

image

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

image

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

image

由于游戲很大,所以第一次搜增加的時(shí)候,會(huì)很卡,而且會(huì)掃很久,我的電腦要掃大概兩分半鐘,而且內(nèi)存會(huì)被吃滿
image

而且掃的次數(shù)多了,會(huì)產(chǎn)生很多臨時(shí)文件
image

像我這樣,C盤直接沒位置了,所以建議備一個(gè)清理C盤的軟件。時(shí)不時(shí)清理臨時(shí)文件

看會(huì)小說或者刷會(huì)短視頻,等掃描結(jié)果出來以后,就再次輸入作弊代碼,就可以將無敵關(guān)閉(我一開始不知道,傻傻得等了五分鐘)。

image

如何查看是否關(guān)閉,看右下角的倒計(jì)時(shí)是否存在。如果沒了,那就是關(guān)閉了。

這時(shí)候搜索減少的數(shù)值。

image

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

image
image
image

快捷鍵,CTRL+D,可以快速查看該地址反匯編地址。

因?yàn)槲宜阉鬟^很多次,所以認(rèn)得特征,當(dāng)反匯編窗口的代碼,是以下這串時(shí)候,我就知道找到了

or [rax],al
image

為什么了,因?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)?

image

但是即使你把它改了,他還是會(huì)變,有時(shí)候會(huì)變?yōu)?code>12,有時(shí)候會(huì)變很大的數(shù),但是你測試一下,將00改為01時(shí)候,在去做一些扣血的行為時(shí)候,是不會(huì)扣血的,達(dá)到了無敵的效果。

基址找到了,那就開始找他的靜態(tài)基址。

一開始嘗試過一步一步找,然后發(fā)現(xiàn),掃出來的基址過多,比較麻煩,所以后面決定使用指針?biāo)阉?/p>

image

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

image

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

image
image

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

image

最終我找出來的基址

"GTA5.exe"+02D06110 +28+298+c8+a8+188五層偏移。

image

代碼編寫

image

游戲是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)
image
image

對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

image

value1:GTA5.exe"+02D06110

image

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

image

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

image

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

image

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

image

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

image

成功讀取到該值,為什么這么寫,因?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)#退出程序

開啟:

image

關(guān)閉:

image

完整代碼

# -*- 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()
image
image
image

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容