如何使用python读写游戏内存以及使用特征码匹配基址

一.读写内存所需的基本参数

接下来我将使用GTA5游戏举例

1.通过进程名称获取进程pid

from psutil import process_iterdef get_process_id_by_name(process_name):for process in process_iter(["pid", "name"]):if process.info["name"] == process_name:return process.info["pid"]return None

 注意进程名称游戏名称不一样,如下:

GTA5.exe是它的进程名称Grand Theft Auto V则是它的游戏名称,在这里我们获取它的pid的使用的是进程名,也就是 GTA5.exe

 2.通过pid获取进程句柄

我们要读写某个进程的时候,都必须先获取它的句柄,在python里面可以使用ctypes完成这一功能,代码如下:

import ctypesPROCESS_ALL_ACCESS = 0x1F0FFF#获取句柄
process = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
#关闭句柄
ctypes.windll.kernel32.CloseHandle(process)

3.游戏基地址

ce里面显示的绿色的地址,都是游戏基地址+一些偏移量构成的地址,这些我们也可以称为基地址。如下所示:

所以图中的 GTA5.exe 则是游戏真正的基地址,那么加号后面的十六进制的数,就是偏移量。这种类型构成的地址都可以称为基地址,但是真正的基地址就一个 那就是 GTA5.exe  

这里ce没有显示GTA5.exe的地址,是因为ce自动给游戏基地址用进程名注册了符号,代替了它的地址。我们可以手动添加,看到GTA5.exe指代的地址具体是什么。如下所示:

 所以7FF7C3020000就是游戏的基地址,那我们如何使用python获取游戏的基地址呢?如下所示:

#方法一:使用win32api的库
import ctypes
from psutil import process_iter
from win32process import EnumProcessModules
from win32api import OpenProcess, CloseHandlePROCESS_ALL_ACCESS = 0x1F0FFFdef get_base_address_by_pid(pid):try:process_handle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)base_address = EnumProcessModules(process_handle)[0]CloseHandle(process_handle)return base_addressexcept:return None#方法二:使用pymem的库
import pymem
def get_base_address_by_name(process_name):try:pm = pymem.Pymem(process_name)base_address = pm.base_addresspm.close_process()return base_addressexcept:return Noneprint(get_base_address_by_pid(get_process_id_by_name("GTA5.exe")))
print(hex(get_base_address_by_pid(get_process_id_by_name("GTA5.exe"))))

两种方法都可以使用,运行结果如下:

由此能看出这个python读取的基地址都是十进制的,我们转换为十六进制就和ce读取的一样了 。

二.读写内存

1.找到需要读取的内存的地址

由于游戏重启以后,系统会分配不同的内存地址给他,因此我们需要找出该动态地址的指针,这里就不讲述如何使用ce查找了,如下所示:

因此我们就需要利用基地址+偏移量的方式,计算出血量所在的地址,用使用偏移量计算地址的代码如下:

import ctypesdef address_by_offsets(base_address, offsets): #base_address表示基地址,offsets表示偏移量列表#获取当前进程的ID(PID)PID = get_process_id_by_name("GTA5.exe")#获取当前进程的句柄process = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, PID) #创建一个缓冲区buffer,缓冲区的大小为ctypes.sizeof(ctypes.c_uint64),并用零填充。
#创建一个无符号64位整型变量bytes_read,用于存储读取的字节数buffer_size = ctypes.sizeof(ctypes.c_uint64)buffer = ctypes.create_string_buffer(buffer_size)bytes_read = ctypes.c_uint64()if base_address != None:for offset in offsets[:-1]:  #读取的过程base_address += offsetctypes.windll.kernel32.ReadProcessMemory(process,ctypes.c_uint64(base_address),buffer,buffer_size,ctypes.byref(bytes_read),)base_address = ctypes.cast(buffer, ctypes.POINTER(ctypes.c_uint64)).contents.valuectypes.windll.kernel32.CloseHandle(process)return base_address + offsets[-1]base_address=get_base_address_by_pid(get_process_id_by_name("GTA5.exe"))
print(address_by_offsets(base_address,[0x025B24D8,0x8,0x280]))
print(hex(address_by_offsets(base_address,[0x025B24D8,0x8,0x280])))

由于我们要知道的是血量的地址,而不是血量的具体值,所以我们读取内存的时候,只需要读取到倒数第二个偏移量即可,根据上面ce的图片显示,它的偏移量就是 [0x025B24D8,0x8,0x280] 也就是读取到前面ce图中的 0x8 即可,最后再加上最后一个偏移量 0x280  则得到 血量的地址 1B0DFAC9220 。相信大家结合上面的两张图就能看懂这段根据偏移量计算最终地址的代码.运行结果如下:

发现转换成16进制以后,和ce显示的地址一样

2.读写内存

通过上一段代码,获取到地址以后,那么我们就可以对指定地址进行读写了,代码如下:

import ctypes
from struct import pack
from ctypes import c_float, c_ubyte, c_ulong#读取内存
def read_memory(address, data_type=ctypes.c_ulong):pid =get_process_id_by_name("GTA5.exe")#进程pidprocess = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)#获取句柄buffer_size = ctypes.sizeof(ctypes.c_uint64)buffer = ctypes.create_string_buffer(buffer_size)bytes_read = ctypes.c_uint64()ctypes.windll.kernel32.ReadProcessMemory(process, ctypes.c_uint64(address), buffer, buffer_size, ctypes.byref(bytes_read))ctypes.windll.kernel32.CloseHandle(process)return ctypes.cast(buffer, ctypes.POINTER(data_type)).contents.value#返回获取的值#写入内存
def write_memory(address, type, data):pid = get_process_id_by_name("GTA5.exe") #进程piddata = pack(type, data) #使用pack函数将需要写入的值打包成相应类型的数据流process = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)if process:buffer_size = len(data)buffer = ctypes.create_string_buffer(data)bytes_written = ctypes.c_uint64()success = ctypes.windll.kernel32.WriteProcessMemory(process,ctypes.c_uint64(address),buffer,buffer_size,ctypes.byref(bytes_written),)ctypes.windll.kernel32.CloseHandle(process)return success != 0 #表示写入成功或者失败else:return False

c_float, c_ubyte, c_ulong,c_ulonglong 分别对应ce里面的 单浮点数类型,字节类型,四字节类型,八字节类型。根据上面的读写,以及计算地址的函数,则可以将函数整合一下。如下所示:

#读取
def read(address, offsets, type):return read_memory(address_by_offsets(address, offsets), type)#写入
def write(address, offsets, type, data):return write_memory(address_by_offsets(address, offsets), type, data)base_address=get_base_address_by_pid(get_process_id_by_name("GTA5.exe"))
print(read(base_address,[0x025B24D8,0x8,0x280],ctypes.c_float))

运行结果如下所示:

到此就完成了读取,写入和这个类似就不多赘述。

3.特征码的匹配

特征码在ce里面是通过字节数组来扫描的,因此查看血量的基地址,如下图:

"GTA5.exe"+025B24D8 则是他的基地址,也就是7FF7C55D24D8 ,它的值则是 1B0DCCB7FE0 ,显然这个值是和血量地址相关的,每次重启游戏,这个值都会变动,显然它的字节数组不能用作特征。因此,我们则需要在内存中找到一个基地址,这基地址的值是和7FF7C55D24D8 相关的。我把这个基地址称为特征地址。特征地址通过一定的运算可以得到我们想要的那个基地址。运算方法如下:

def calculate_ptr(address):return address + read_memory(address + 3) + 7

通过一个简单的debug,就可以计算出我们想要的血量的特征地址是什么。代码如下:

print('debug开始')
base_address #用前面讲到的函数获取
start=0x000000
end=0x2000000test="0x7ff7c55d24d8" #血量的基地址
def slove(part0,part1):print('计算开始')for i in range(part0,part1):result=Memory.calculate_ptr(base_address+i)if hex(result)==test:#0x7ff7e95551c4print(hex(i))print(hex(result))print('计算结束')returnprint('没有结果')slove(start,end)

结果如下:

发现"GTA5.exe"+0x7ab9 则是我们想要的特征地址,将他放入ce中查看字节数组。如下所示:

这样我们就获得了我们需要的特征码,那么怎么找出特征码中的正确的特征呢?使用ce反汇编该区域查看。如下所示:

 

 通过分析得到  48 8B 0D ?? ?? ?? ?? 8B C3 ?? ?? ?? 74 为特征码 ,在ce中搜索该字节数组,如果结果唯一,那么该特征码可以使用。如图所示:

 那么如何在python里面实现特征码的匹配呢?代码如下所示:

import pymem
#将特征码转换成字节流
def convert_pattern_to_regex(byte_pattern):pattern_parts = byte_pattern.split()regex_pattern = b""for part in pattern_parts:if part == "??":regex_pattern += b"."else:regex_pattern += bytes.fromhex(part)# print(regex_pattern)return regex_pattern#模式串匹配,这里使用了pymem库完成
def match_pattern(process_name, module_name, byte_pattern):pm = pymem.Pymem(process_name)module = pymem.process.module_from_name(pm.process_handle, module_name)if not module:print(f"Module {module_name} not found in process {process_name}")return Noneregex_pattern = convert_pattern_to_regex(byte_pattern)addresses = pymem.pattern.pattern_scan_module(pm.process_handle, module, regex_pattern, return_multiple=True)if addresses:for address in addresses:print(f"Pattern found at address: 0x{address:X}")return addresseselse:print("Pattern not found")return None
match_pattern("GTA5.exe", "GTA5.exe", "48 8B 0D ?? ?? ?? ?? 8B C3 ?? ?? ?? 74")

结果如下:

成功匹配到特征地址,此时通过上面的calucuteptr则可以计算出血量的基地址。代码如下:

pattern_address=match_pattern("GTA5.exe", "GTA5.exe", "48 8B 0D ?? ?? ?? ?? 8B C3 ?? ?? ?? 74")
health_base=calculate_ptr(pattern_address[0])
print(hex(health_base))
health=read(health_base,[0x0,0x8,0x280],ctypes.c_float)
print(health)

为什么现在偏移从前面的 [0x025B24D8,0x8,0x280] 变成了现在的 [0x0,0x8,0x280],是因为刚开始的read的第一个参数base_address 是 GTA5.exe ,而现在的base_address则是我们已经算出来的那个基地址也就是GTA5.exe+0x7ab9,所以最开始就不用偏移了,所以偏移量就是0x0。结果如下:

到此,使用python完成读写,以及模式串就都完成了。

感谢你的观看,如果有疑问,可以回复我,我可以一一回答 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/891147.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简述css中z-index的作用?如何用定位使用?

z-index是一个css属性,用于控制元素的堆叠顺序, 如何使用定位用index 1、position:relative; z-index; 相对于自己来定位的,可以根据top,bottom,right,left&#xff…

Playwright爬虫xpath获取技巧

示例一 <button class"MuiButtonBase-root MuiButton-root MuiLoadingButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeLarge MuiButton-containedSizeLarge MuiButton-colorPrimary MuiButton-fullWidth MuiButton-root MuiLoadingButton…

Java中处理if-else的几种高级方法

前言 在我看来多写几个if-else没啥大不了的&#xff0c;但是就是看起来没啥逼格&#xff0c;领导嫌弃。我根据开发的经历写几个不同的替代方法 一、枚举法替代 我先前写了一篇文章&#xff0c;可以去看看。 通过枚举替换if-else语句的解决方案_枚举代替if else c语言-CSDN博…

CCNP_SEC_ASA 第六天作业

实验需求&#xff1a; 为保障内部用户能够访问Internet&#xff0c;请把10.1.1.0/24网络动态转换到外部地址池202.100.1.100-202.100.1.200&#xff0c;如果地址池耗尽后&#xff0c;PAT到Outside接口 提示&#xff1a;需要看到如下输出信息 Inside#telnet 202.100.1.1 Trying …

计算机网络 (13)信道复用技术

前言 计算机网络中的信道复用技术是一种提高网络资源利用率的关键技术。它允许在一条物理信道上同时传输多个用户的信号&#xff0c;从而提高了信道的传输效率和带宽利用率。 一、信道复用技术的定义 信道复用&#xff08;Multiplexing&#xff09;就是在一条传输媒体上同时传输…

敏捷开发Scrum的深入理解和实践

敏捷开发&#xff0c;特别是Scrum方法&#xff0c;已经逐渐成为软件开发领域的主流方法。Scrum不仅适用于软件开发&#xff0c;还适用于其他需要快速响应变化和灵活交付的领域。本文将深入探讨Scrum的核心概念、流程、优势、挑战及其在实践中的应用。 一、Scrum的核心概念 Scru…

计算机视觉目标检测-2

文章目录 摘要abstract1.Fast R-CNN1.1 RoI pooling1.2 End-to -End model1.3 多任务损失-Multi-task loss1.4 R-CNN、SPPNet、Fast R-CNN效果比对 2.Faster R-CNN2.1 RPN原理2.2 效果对比2.3 Faster R-CNN总结 3.总结4.参考文献 摘要 本周学习了Fast R-CNN和Faster R-CNN算法…

JavaScript网页设计案例:动态交互式任务列表

在现代网页开发中&#xff0c;JavaScript被广泛应用于实现动态交互效果。看完这一篇你就可以设计一个动态任务列表&#xff0c;全面展示HTML、CSS和JavaScript在前端开发中的实际应用。通过本案例&#xff0c;你将深入了解事件监听、DOM操作以及用户交互设计的实现过程。 案例需…

【MySQL】索引 面试题

文章目录 适合创建索引的情况创建索引的注意事项MySQL中不适合创建索引的情况索引失效的常见情况 索引定义与作用 索引是帮助MySQL高效获取数据的有序数据结构&#xff0c;通过维护特定查找算法的数据结构&#xff08;如B树&#xff09;&#xff0c;以某种方式引用数据&#xf…

使用Excel制作通达信自定义“序列数据“

序列数据的视频教程演示 Excel制作通达信自定义序列数据 1.序列数据的制作方法&#xff1a;删掉没有用的数据&#xff08;行与列&#xff09;和股代码格式处理&#xff0c;是和外部数据的制作方法是相同&#xff0c;自己上面看历史博文。只需要判断一下&#xff0c;股代码跟随的…

【ELK】ES单节点升级为集群模式--太细了!

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言准备工作1. 查看现状【单节点】2. 原节点改集群模式3. 改es配置文件&#xff0c;增加集群相关配置项4. *改docker映射的端口* 启动新节点5. docker-compose起一…

关于区块链的安全和隐私

背景 区块链技术在近年来发展迅速&#xff0c;被认为是安全计算的突破&#xff0c;但其安全和隐私问题在不同应用中的部署仍处于争论焦点。 目的 对区块链的安全和隐私进行全面综述&#xff0c;帮助读者深入了解区块链的相关概念、属性、技术和系统。 结构 首先介绍区块链…

webauthn介绍及应用

1、webauthn介绍 官网&#xff1a;https://webauthn.io/ 1.1、什么是webauthn&#xff1f; webauthn即Web Authentication&#xff0c;是一个符合W3C标准的Web认证规范。它通过公私钥加密技术&#xff0c;实现无密码认证&#xff0c;用户仅需通过pin码、指纹、面部识别、usb …

ElasticSearch如何做性能优化?

大家好&#xff0c;我是锋哥。今天分享关于【ElasticSearch如何做性能优化?】面试题。希望对大家有帮助&#xff1b; ElasticSearch如何做性能优化? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Elasticsearch 是一个开源的分布式搜索引擎&#xff0c;广泛用于…

Python 高级游戏开发:构建一个基于 Pygame 的多人在线战斗游戏

在本篇文章中,我们将介绍如何使用 Python 和 Pygame 库开发一个具有多人在线功能的高级游戏。我们将涵盖如何搭建游戏服务器,如何实现客户端-服务器模型,以及如何处理网络通信和游戏逻辑。这不仅能帮助你提升游戏开发技能,还能加深对 Python 网络编程的理解。 一、准备工作…

瑞芯微全新芯片平台RK3506优势详解,高集成低功耗,为工业而生 触觉智能测评

RK3506是瑞芯微Rockchip在2024年第四季度全新推出的Arm嵌入式芯片平台&#xff0c;三核Cortex-A7单核Cortex-M0多核异构设计&#xff0c;CPU频率达1.5Ghz, M0 MCU为200Mhz。 而RK3506芯片平台下的工业级芯片型号RK3506J&#xff0c;具备-40-85℃的工业宽温性能、发热量小&#…

CentOS7 初始化模板

一、下载Centos7的qcow2镜像 CentOS Cloud Images qemu-img.exe convert -f qcow2 -O vmdk CentOS-7-x86_64-GenericCloud-2211.qcow2 centos7.vmdk 二、更改root密码 注意由于有cloud init的原因&#xff0c;按网上进入救援模式的办法无法修改密码&#xff0c;会卡住不动。…

Docker 安装全攻略:从入门到上手

Docker 安装全攻略&#xff1a;从入门到上手 在当今的软件开发与部署领域&#xff0c;Docker 已经成为了一项不可或缺的关键技术。它能够将应用程序及其依赖项打包成轻量级、可移植的容器&#xff0c;极大地简化了开发、测试和部署的流程。本文将详细讲解在不同操作系统下 Doc…

代码随想录算法训练营第六十天 | 图 | A星算法

Day 60 总结 自己实现中遇到哪些困难今日收获&#xff0c;记录一下自己的学习时间 13:00 - 14:00 BFS 题目&#xff1a;127. 骑士的攻击 给定两个坐标&#xff0c;搜索最短路径 使用 BFS&#xff0c;广度搜索&#xff0c;按层搜索找到最短路径 public class Main {public…

【 Sonarqube】可视化Java项目单元测试覆盖率统计框架搭建

一、项目背景&#xff1a; 一个小公司的朋友反应他们那边Java项目单元测试有&#xff0c;但还没有可视化统计覆盖率数据&#xff0c;没法统计就不能直观的看到单测的覆盖率&#xff0c;Java的覆盖率统计框架还是比较成熟&#xff0c;部署起来也不是很难&#xff0c;下面我们逐…