python脚本扫描win系统中运行程序内存值并修改

 用于修改运行中程序的内存值

#!encoding=utf8

import os
import json
import psutil
import shutil
import threading
import ctypes
import win32api 
import win32process
from win32con import PROCESS_ALL_ACCESS 

class MemScan:
    '''
    注:一定要以系统管理员身份运行脚本(管理员身份运行cmd) 
    windows系统 根据 进程号 pid 进行扫描内存中的数据, 最终定位到需要修改的内存地址并设置新值, 已知内存地址可以跳过扫描步骤

    定位内存地址的方法:
    一、精确查找
        1、在目标程序页面找到需要修改的值 对该值进行首次扫描
        2、在目标程序中改变这个值, 用新值进行二次扫描(扫描内存地址范围为上一次扫描结果)
        3、如果还是没办法确定准确的内存地址, 则重复第 2 步 逐步缩小范围
        4、根据内存地址设置新值
    '''
    def __init__(self, pid):
        self.scan_data = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'scan_data.json')
        self.tmp_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'tmp')
        self.hProcess = win32api.OpenProcess(PROCESS_ALL_ACCESS, False, pid)  # 获取进程句柄
        # 根据进程号来获取进程的内存大小
        process = psutil.Process(pid)
        mem_info = process.memory_info()
        print("内存总量:", mem_info.rss)  #  resident set size, 常驻内存集大小
        print("内存占用的峰值:", mem_info.vms)  # virtual memory size, 虚拟内存大小
        self.base_addr = win32process.EnumProcessModules(self.hProcess)[0]
        self.end_addr =  mem_info.vms + self.base_addr
        print(f'pid:{pid} 的进程基址:{self.base_addr}  内存结束地址:{self.end_addr}')
        
    def read(self, mem_address, data_len=4, data_type=''):
        '''
        读取内存中的值
        '''
        if data_type == 'int':
            addr = ctypes.c_int32()
        elif data_type == 'float':
            addr = ctypes.c_float()
        elif data_type == 'double':
            addr = ctypes.c_double()
            data_len = 8
        else:
            addr = ctypes.c_ulong()
        # 句柄,地址,返回值,长度
        ctypes.windll.kernel32.ReadProcessMemory(int(self.hProcess), ctypes.c_void_p(int(mem_address)), ctypes.byref(addr), data_len, None)
        # print(addr)
        return addr.value
 
    def write(self, mem_address, num, data_len=4, data_type=''):
        '''
        根据内存地址写入值
        '''
        if data_type == 'int':
            addr = ctypes.c_int32()
        elif data_type == 'float':
            addr = ctypes.c_float()
        elif data_type == 'double':
            addr = ctypes.c_double()
            data_len = 8
        else:
            addr = ctypes.c_ulong()
        addr.value = num
        # 句柄,地址,写入值,长度
        return ctypes.windll.kernel32.WriteProcessMemory(
            int(self.hProcess), ctypes.c_void_p(int(mem_address)), ctypes.byref(addr), data_len, None
        )

    def _scan(self, target_value, scan_addrs, data_len=4, data_type=''):
        '''
        target_value: 目标值
        scan_addrs: 待扫描的地址列表
        data_len: 数据长度
        data_type: 数据类型
        '''
        current_thread = threading.current_thread()
        thread_id = current_thread.ident
        # print(f'当前线程ID:{thread_id}')
        current_save_path = os.path.join(self.tmp_dir, f'scan_data_{thread_id}.json')

        if data_type == 'int':
            addr = ctypes.c_int32()
        elif data_type == 'float':
            addr = ctypes.c_float()
        elif data_type == 'double':
            addr = ctypes.c_double()
            data_len = 8
        else:
            addr = ctypes.c_ulong()

        new_scan_data = {}

        for mem_addr in scan_addrs:
            value = self.read(mem_addr, data_len, data_type)
            if value == target_value:
                print(f'内存地址:{mem_addr}, 值为:{value}')
                mem_data = {'first_value': value, 'last_value': None ,'current_value': value}
                new_scan_data[mem_addr] = mem_data

        if new_scan_data:
            # print(f'扫描结果保存路径:{current_save_path}')
            json.dump(new_scan_data, open(current_save_path, 'w', encoding='utf8'))

    def scan(self, target_value, data_len=4, data_type='', is_first_scan=False, thread_num=10):
        '''
        target_value: 目标值
        data_len: 数据长度
        data_type: 数据类型
        is_first_scan: 是否是第一次扫描
        thread_num: 线程数量
        '''
        if os.path.exists(self.tmp_dir):
            shutil.rmtree(self.tmp_dir)
        os.makedirs(self.tmp_dir)

        old_scan_data = {}
        mem_addrs = []
        if is_first_scan:
            mem_addrs = list(range(self.base_addr, self.end_addr, data_len))
        else:
            try:
                old_scan_data = json.load(open(self.scan_data, 'r', encoding='utf8'))
                mem_addrs = list(old_scan_data.keys())
            except:
                print(f'获取上一次扫描结果失败,请确认是否存在上一次扫描结果,或开启首次扫描')

        step = int(len(mem_addrs)/thread_num)
        step = step if step > 0 else 1
        sub_mem_addrs = [mem_addrs[i:i+step] for i in range(0, len(mem_addrs), step)]

        threads = []
        for sub_mem_addrs in sub_mem_addrs:
            t = threading.Thread(target=self._scan, args=(target_value, sub_mem_addrs, data_len, data_type))
            threads.append(t)
            t.start()
        for t in threads:
            t.join()

        new_scan_data = {}
        for filename in os.listdir(self.tmp_dir):
            filepath = os.path.join(self.tmp_dir, filename)
            new_scan_data.update(json.load(open(filepath, 'r', encoding='utf8')))
        
        for k, v in new_scan_data.items():
            old_v = old_scan_data.get(k, None)
            if old_v is not None:
                v['last_value'] = old_v['current_value']
                v['first_value'] = old_v['first_value']

        print(f'扫描结果保存路径:{self.scan_data}')
        json.dump(new_scan_data, open(self.scan_data, 'w', encoding='utf8'))
    
    def set_value_by_mem_addr(self, mem_addr, value, data_len=4, data_type=''):
        '''
        根据内存地址写入值
        mem_addr: 内存地址  10进制内存地址
        value: 写入的值
        data_len: 数据长度
        data_type: 数据类型
        '''
        if data_type == 'double':
            data_len = 8

        src = self.read(mem_addr, data_len, data_type)
        self.write(mem_addr, value, data_len, data_type)
        dst = self.read(mem_addr, data_len, data_type)

        print(f'目标地址:{mem_addr}, 原始值:{src}, 新值:{value}, 写入后值:{dst}')

if __name__ == '__main__':

    # 替换为目标进程的PID , 可通过 任务管理器-详细信息 查看
    pid = 3960  

    # 扫描的数据长度     int, float 是4字节,double 是8字节
    data_len = 4
    # 扫描的数据类型    int, float 是4字节,double 是8字节
    data_type = 'int'
    # 扫描并发数量
    thread_num = 100

    ms = MemScan(pid)

    # 第一次扫描
    target_value = 1092
    ms.scan(target_value, data_len, data_type, thread_num=thread_num, is_first_scan=True)

    # 第N次扫描
    # target_value = 1090
    # ms.scan(target_value, data_len, data_type, thread_num=thread_num)

    # 内存地址设置值
    # target_value = 1090
    # mem_addr = 27366160
    # ms.set_value_by_mem_addr(mem_addr, target_value, data_len, data_type)

寻找PID

第一次扫描结果,值为1090的有很多

第二次扫描结果,通过程序修改1090的值为1085后,扫描结果只有1个

设置新值,根据内存地址设置值,设置完成后可以通过程序页面查看是否同步修改了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值