吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

搜索
查看: 5898|回复: 71

[游戏安全] Python64位游戏读写--GTA锁血

  [复制链接]
我要学外挂 发表于 2020-6-23 09:43
本帖最后由 我要学外挂 于 2020-6-23 11:05 编辑

37.jpg
EPIC送了GTA5,最近考完试和朋友一起玩了下,由于刚开始,等级低,朋友带我打任务的时候,完全就打不过,我的甲没几下就爆了,刚开始没什么。

但是今天去打末日3的时候,由于只有两个人,玩过的都知道,末日3是需要一个人去玩小游戏,然后其他人保护玩小游戏那个队友,末日3的NPC是会一直刷,怎么打都打不完,有一瞬间就变成了CSGO练爆头模式,完全没得打(打不过就充钱,没得充钱就变强)。后面搜了搜,发现别人歪瓜的截图,有锁血功能,就想去了解一下。

打开GTAV,进入故事模式。GTAV官方在故事模式中,有给出作弊代码,其中有一个无敌五分钟的命令。从这个命令入手,查找他的基址,没准能用。

作弊代码:PAINKILLER

搜索思路
👇
开启无敌时候,可能会有数值增加,可能有一个数值或者开关,会变为1。这时搜索增加的数值
关闭无敌时候,可能会有数值减少,可能有一个数值或者开关,会变为0。这时搜索减少的数值

开启无敌时候,可能会有数值减少,可能有一个数值或者开关,某个数值减少。这时搜索减少的数值
关闭无敌时候,可能会有数值减少,可能有一个数值或者开关,某个数值增加。这时搜索增加的数值
👆

搜索基址

36.jpg
CE加载GTA5,先通过常规思路,无敌搜增加,关闭搜减少。
1.png

即使这游戏是64位的,但是依然扫4字节就够了。首先先扫一次未知的初始值。
2.png
进入GTA,按~键,输入作弊代码PAINKILLER
3.png
输入成功时候,右下角会有无敌时间,正常为五分钟。
4.png
这时候去CE搜索增加的数值
5.png
由于游戏很大,所以第一次搜增加的时候,会很卡,而且会扫很久,我的电脑要扫大概两分半钟,而且内存会被吃满
19.png
而且扫的次数多了,会产生很多临时文件
20.png
像我这样,C盘直接没位置了,所以建议备一个清理C盘的软件。时不时清理临时文件

看会小说或者刷会短视频,等扫描结果出来以后,就再次输入作弊代码,就可以将无敌关闭(我一开始不知道,傻傻得等了五分钟)。

如何查看是否关闭,看右下角的倒计时是否存在。如果没了,那就是关闭了。

这时候搜索减少的数值。
7.png
步骤循环,等结果少于5000的时候。就可以通过搜索未变动的数值,来开始查找最终的基址。
8.png
9.png
10.png
快捷键,CTRL+D,可以快速查看该地址反汇编地址。

因为我搜索过很多次,所以认得特征,当反汇编窗口的代码,是以下这串时候,我就知道找到了

or [rax],al

12.png
为什么了,因为这个al在线上模式时候,他是0或者1,0就是关闭无敌,1就是开启无敌。

一开始我怎么找到这个的了,我当时扫了很多次,发现有一次,他的值不是al,而是0,然后当我开启无敌时候,他就变为1了。我就把它认住了。发现有的时候在线下模式,他就是会这样,抽风。

关闭无敌的默认初始值是:4194316

当你把他的数值改为这个初始值时,他这个al就会变为0
13.png
但是即使你把它改了,他还是会变,有时候会变为12,有时候会变很大的数,但是你测试一下,将00改为01时候,在去做一些扣血的行为时候,是不会扣血的,达到了无敌的效果。

基址找到了,那就开始找他的静态基址。

一开始尝试过一步一步找,然后发现,扫出来的基址过多,比较麻烦,所以后面决定使用指针搜索
14.png
我这里没调参数,直接确定了。
15.png
最后扫出来的结果,有9419539个,观察他的值,值等于初始值4194316或等于基址目前的值的时候,就把它双击,添加到地址表中。
16.png
17.png
一定要选多几个,因为进入线上模式时候,值会变,有的会变成?号,有的是不知道啥值,所以要选多几个加以观察,或者不关闭指针扫描结果窗口。
18.png
最终我找出来的基址

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

38.jpg


代码编写

40.jpg
游戏是64位的,所以要用64位的函数。

WOW64 函数

[b]ZwWow64QueryInformationProcess64
ZwWow64ReadVirtualMemory64
ZwWow64WriteVirtualMemory64
ZwWow64CallFunction64
32位程序几乎可以获取64位程序的全部函数调用功能。

首先导入函数

基本操作都没啥不同的。

# -*- coding:utf-8 -*-
"""
@author: 
@file: GTA.py
@time: 2020-06-21 13:49
@desc: KeyboArd
"""
import win32process#进程模块
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")#调用ntdll库
kernel32 = ctypes.windll.LoadLibrary("kernel32.dll")#调用kernel32库
GetLastError = kernel32.GetLastError#调用GetLastError函数
GTA = 0x7FF79B5D0000#GTA.EXE的基址,原本是打算通过模块名字获取的,但是不知道为什么返回-1.可能是64位游戏,涉及到一些权限问题,后面在研究一下,目前主要是为了达到目的。
STANDARD_RIGHTS_REQUIRED = 0x000F0000#这两个的值,是在C++那里获取过来的。
SYNCHRONIZE = 0x00100000#看图
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)#看图

class PROCESS_BASIC_INFORMATION(ctypes.Structure):
    _fields_ = [('ExitStatus', ctypes.c_ulonglong),     # 接收进程终止状态
                ('PebBaseAddress', ctypes.c_ulonglong),  # 接收进程环境块地址
                ('AffinityMask', ctypes.c_ulonglong),  # 接收进程关联掩码
                ('BasePriority', ctypes.c_ulonglong),  # 接收进程的优先级类
                ('UniqueProcessId', ctypes.c_ulonglong),  # 接收进程ID
                ('InheritedFromUniqueProcessId', ctypes.c_ulonglong)]  # 接收父进程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"Gra***** A****V")
_hGameHandle = _GetPorcessHandle(ProcessId)

23.png
24.png

对64位内存读操作

def _ReadMemeryLong64(addr, bufflength):
    addr = c_ulonglong(addr)#64位游戏,我测试过,如果只写c_ulong() 会报错,说有冲突。
    ret = c_ulonglong()#所以这里也都保持同样的长度把
    BufferLength = c_ulonglong(bufflength)
    ntdll.ZwWow64ReadVirtualMemory64(int(_hGameHandle), addr, byref(ret), BufferLength, 0)#ZwWow64ReadVirtualMemory64和ReadProcessInt 这个函数操作类似。
    return ret.value

64位通过模块名获取基址

def GetBaseAddr(ModuleName):  #这是论坛里一位大佬写的,为了方便快捷,先复制粘贴过来,这代码我也没看懂,好像涉及到了pe知识,后面自己在 写一个。
    # 传入需要查找的模块的名称,就可以返回相应的模块基址了
    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))
    """
    这同样是一个未公开的api,可以通过他获取进程的信息,然后存入我们一开始定义的结构体中,他的五个参数分别是:
    进程句柄,信息类型,缓冲指针,以字节为单位的缓冲大小, 写入缓冲的字节数
    而至于下面为什么要这么写,其实涉及到了程序的PE结构,这里不做赘述,因为这个东西不是一会会说的清楚的,可以自行百度
    """
    ret = _ReadMemeryLong64(Buffer.PebBaseAddress + 24, 8)
    ret = _ReadMemeryLong64(ret + 24, 8)

    for i in range(100000):  # 这里用for循环其实是怕程序卡死,下面如果出了问题不能退出的话,循环结束一样可以退出
        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):
    #"GTA5.exe"+02D06110 +28+298+c8+a8+188  这是找到的基址
    value = GTA+ 0x02D06110#GTA的基址等于0x7FF79B5D0000 ,如果重启了电脑,这个基址会变,所以一定要写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
25.png
value1:GTA5.exe"+02D06110
26.png
value2:GTA5.exe"+02D06110+0x28
27.png
value3:GTA5.exe"+02D06110+0x28+0x298
28.png
value4:GTA5.exe"+02D06110+0x28+0x298+0XC8
29.png
value5:GTA5.exe"+02D06110+0x28+0x298+0XC8+0xa8
30.png
value6:GTA5.exe"+02D06110+0x28+0x298+0XC8+0xa8+0x188
31.png
成功读取到该值,为什么这么写,因为一开始我直接省略麻烦,写一堆,发现读的都是什么东西,太乱了。这样子容易找到问题。

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这个函数相似

无敌

初始:4194316
无敌:4194572

def set_invincible(hGameHandle,address):
    while True:
        WriteMemeryLong64(hGameHandle,address+0x188,4194572,4)#开启无敌

        if(win32api.GetAsyncKeyState(35)!=0):#当按下END键,就关闭无敌,将值写回正常
            WriteMemeryLong64(hGameHandle, address + 0x188, 4194316, 4)#关闭无敌
            CloseHandle(hGameHandle)#关闭句柄
            exit(0)#退出程序

开启:
32.png

关闭:
33.png

完整代码

# -*- coding:utf-8 -*-
"""
@author: 
@file: GTA.py
@time: 2020-06-21 13:49
@desc: KeyboArd
"""
import win32process#进程模块
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),     # 接收进程终止状态
                ('PebBaseAddress', ctypes.c_ulonglong),  # 接收进程环境块地址
                ('AffinityMask', ctypes.c_ulonglong),  # 接收进程关联掩码
                ('BasePriority', ctypes.c_ulonglong),  # 接收进程的优先级类
                ('UniqueProcessId', ctypes.c_ulonglong),  # 接收进程ID
                ('InheritedFromUniqueProcessId', ctypes.c_ulonglong)]  # 接收父进程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"G*********o 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):#这个函数用以读取模块名称,与ReadVirtualMemory64不同的点还有一个是我们会传入一个缓冲区的长度length,用于定义一个c_wchar_p的缓冲区
    addr = c_ulonglong(addr)
    ret = c_wchar_p("0" * length)#这里选用wchar其实与编码有关,感兴趣的同学自行百度wchar,宽字符等关键字学习
    BufferLength = c_ulonglong(n)
    ntdll.NtWow64ReadVirtualMemory64(int(_hGameHandle), addr, ret, BufferLength, 0)
    return ret.value

def GetBaseAddr(ModuleName):  #
    # 传入需要查找的模块的名称,就可以返回相应的模块基址了
    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))
    """
    这同样是一个未公开的api,可以通过他获取进程的信息,然后存入我们一开始定义的结构体中,他的五个参数分别是:
    进程句柄,信息类型,缓冲指针,以字节为单位的缓冲大小, 写入缓冲的字节数
    而至于下面为什么要这么写,其实涉及到了程序的PE结构,这里不做赘述,因为这个东西不是一会会说的清楚的,可以自行百度
    """
    ret = _ReadMemeryLong64(Buffer.PebBaseAddress + 24, 8)
    ret = _ReadMemeryLong64(ret + 24, 8)

    for i in range(100000):  # 这里用for循环其实是怕程序卡死,下面如果出了问题不能退出的话,循环结束一样可以退出
        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)#关闭无敌
            CloseHandle(hGameHandle)
            exit(0)

def main():
    moudle = GetBaseAddr("****5.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()

34.png
35.jpg 39.jpg


11.png
6.png

免费评分

参与人数 33威望 +1 吾爱币 +55 热心值 +29 收起 理由
caozhe01 + 1 + 1 谢谢@Thanks!
illyawolaopo + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!有点复杂慢慢消化一下
EscapeUtopia + 1 + 1 谢谢@Thanks!
Dmdmdms + 1 + 1 用心讨论,共获提升!
jFae + 1 + 1 我很赞同!
追梦de鹏 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
yourenzhang + 1 谢谢@Thanks!
woyucheng + 1 + 1 谢谢@Thanks!
梦古无疆 + 1 + 1 我很赞同!
LZ01 + 1 我很赞同!
nws0507 + 1 + 1 谢谢@Thanks!
Games游戏迷 + 1 + 1 热心回复!
linso + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
kicebeauty + 1 + 1 用心讨论,共获提升!
hj170520 + 1 + 1 谢谢@Thanks!
hmxfj + 1 + 1 谢谢@Thanks!
vethenc + 1 + 1 谢谢@Thanks!
GYZ1028 + 1 用心讨论,共获提升!
江南第一帅 + 1 + 1 我很赞同!
Forf丶T + 1 + 1 热心回复!
wkfy + 2 + 1 热心回复!
Xjl955666 + 1 + 1 GTA5菜鸡前来送分。
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
culprit + 1 + 1 我很赞同!
laoxiaodiao + 1 + 1 用心讨论,共获提升!
MaxMadcc + 1 谢谢@Thanks!
风生·水起 + 2 + 1 问下,64位修改虚拟保护的是哪个api
RyanEdward + 1 + 1 谢谢@Thanks!
burning + 1 用心讨论,共获提升!
yuehanoo + 1 + 1 谢谢@Thanks!
xiaosuobjsd + 1 + 1 白嫖GTA5党送分
小朋友呢 + 2 + 1 用心讨论,共获提升!
nirunyu + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

mAsk° 发表于 2020-6-23 13:31
无敌模式正确的基址应该是WorldPTR+0x8+0x189

WorldPTR视游戏版本而定,Steam是GTA5.exe+0x24B0C50,Epic是GTA5.exe+0x24AECE0

还有,无敌模式数据类型应该是BYTE,也就是1个字节,1是开,0是关。而不是楼主程序里写的4字节

更多GTAV外挂相关知识请看我的Github开源项目:https://github.com/AmazingPP/subVerison_GTAV_Hack

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
samvon + 1 + 1 谢谢@Thanks!

查看全部评分

mAsk° 发表于 2020-6-23 21:18
风生·水起 发表于 2020-6-23 10:42
谁知道64位修改虚拟保护的是哪个api? 我试过32位里面的VirtualProtect,没有效果。
有些内存地址访问权限 ...

64位程序直接用VirtualProtectEx即可,32位可以LoadLibrary加载64位的ntdll.dll,然后用里面的NtProtectVirtualMemory

具体实现可以看:https://github.com/rwfpl/rewolf-wow64ext
zucker 发表于 2020-6-23 09:54
吾爱不解 发表于 2020-6-23 09:59
楼主  我也领了游戏 但是R星服务器 总是登录不上 怎么办~~!
YuerWang 发表于 2020-6-23 10:01
楼主太强了!
xiaosuobjsd 发表于 2020-6-23 10:02
想当年我也参与帮助过一个网友整过这个,不知道他做出来没有@-巫枫
nirunyu 发表于 2020-6-23 10:04
厉害,学到了
羋羋羋 发表于 2020-6-23 10:07
还有这种操作,大佬666啊
yuehanoo 发表于 2020-6-23 10:07
现在下载不了GTA5了,各大单机网站都关闭下载了,有也没用,不过也要支持下。
xiahhhr 发表于 2020-6-23 10:09
python写这类东西代码量确实太多了,不过这名字是不是暴露了
judgecx 发表于 2020-6-23 10:11
楼主这就很六啊 不过名字我爱了哈哈
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2020-8-14 17:00

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表