You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python进程内存读取检测作弊代码故障排查求助(附代码)

进程内存中'exechack.cc'字符串检测代码修正方案

原代码核心问题分析

  1. PROCESSENTRY32结构体定义错误:原代码未正确定义进程快照所需的结构体字段,导致无法正确枚举进程ID。
  2. 内存区域过滤条件过窄:仅筛选PAGE_READWRITE权限的内存区域,会遗漏存放字符串的只读内存段(如程序的只读数据区)。
  3. 缺少必要的错误检查:未对系统API调用的返回结果做详细错误排查,难以定位问题。

修正后的完整代码

import ctypes
from ctypes import wintypes

# 定义常量
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010
MEM_COMMIT = 0x1000
PAGE_READONLY = 0x02
PAGE_READWRITE = 0x04
PAGE_WRITECOPY = 0x08
PAGE_EXECUTE_READ = 0x20
PAGE_EXECUTE_READWRITE = 0x40
PAGE_EXECUTE_WRITECOPY = 0x80

# 正确定义PROCESSENTRY32结构体
class PROCESSENTRY32(ctypes.Structure):
    _fields_ = [
        ("dwSize", wintypes.DWORD),
        ("cntUsage", wintypes.DWORD),
        ("th32ProcessID", wintypes.DWORD),
        ("th32DefaultHeapID", wintypes.LPVOID),
        ("th32ModuleID", wintypes.DWORD),
        ("cntThreads", wintypes.DWORD),
        ("th32ParentProcessID", wintypes.DWORD),
        ("pcPriClassBase", wintypes.LONG),
        ("dwFlags", wintypes.DWORD),
        ("szExeFile", ctypes.c_char * 260)
    ]

class MEMORY_BASIC_INFORMATION(ctypes.Structure):
    _fields_ = [
        ("BaseAddress", ctypes.c_void_p),
        ("AllocationBase", ctypes.c_void_p),
        ("AllocationProtect", wintypes.DWORD),
        ("RegionSize", ctypes.c_size_t),
        ("State", wintypes.DWORD),
        ("Protect", wintypes.DWORD),
        ("Type", wintypes.DWORD)
    ]

# 加载kernel32并设置函数参数类型
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
kernel32.CreateToolhelp32Snapshot.argtypes = [wintypes.DWORD, wintypes.DWORD]
kernel32.CreateToolhelp32Snapshot.restype = wintypes.HANDLE

kernel32.Process32First.argtypes = [wintypes.HANDLE, ctypes.POINTER(PROCESSENTRY32)]
kernel32.Process32First.restype = wintypes.BOOL

kernel32.Process32Next.argtypes = [wintypes.HANDLE, ctypes.POINTER(PROCESSENTRY32)]
kernel32.Process32Next.restype = wintypes.BOOL

kernel32.OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]
kernel32.OpenProcess.restype = wintypes.HANDLE

kernel32.VirtualQueryEx.argtypes = [wintypes.HANDLE, wintypes.LPCVOID, ctypes.POINTER(MEMORY_BASIC_INFORMATION), ctypes.c_size_t]
kernel32.VirtualQueryEx.restype = ctypes.c_size_t

kernel32.ReadProcessMemory.argtypes = [wintypes.HANDLE, wintypes.LPCVOID, wintypes.LPVOID, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t)]
kernel32.ReadProcessMemory.restype = wintypes.BOOL

kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
kernel32.CloseHandle.restype = wintypes.BOOL

def find_exechack(process_name):
    process_ids = []
    # 创建进程快照
    snapshot = kernel32.CreateToolhelp32Snapshot(0x00000002, 0)  # TH32CS_SNAPPROCESS
    if snapshot == wintypes.HANDLE(-1).value:
        print(f"CreateToolhelp32Snapshot失败,错误码: {ctypes.get_last_error()}")
        return

    pe32 = PROCESSENTRY32()
    pe32.dwSize = ctypes.sizeof(PROCESSENTRY32)
    
    if not kernel32.Process32First(snapshot, ctypes.byref(pe32)):
        print(f"Process32First失败,错误码: {ctypes.get_last_error()}")
        kernel32.CloseHandle(snapshot)
        return

    # 枚举进程
    while True:
        exe_name = pe32.szExeFile.decode('gbk', errors='ignore')  # 适配中文进程名
        if exe_name == process_name:
            process_ids.append(pe32.th32ProcessID)
        
        if not kernel32.Process32Next(snapshot, ctypes.byref(pe32)):
            # 检查是否是正常结束
            if ctypes.get_last_error() != 18:  # ERROR_NO_MORE_FILES
                print(f"Process32Next失败,错误码: {ctypes.get_last_error()}")
            break

    kernel32.CloseHandle(snapshot)

    # 遍历进程搜索内存
    target_str = b'exechack.cc'
    target_len = len(target_str)

    for pid in process_ids:
        # 打开进程,申请足够权限
        process_handle = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid)
        if not process_handle:
            print(f"打开进程{pid}失败,错误码: {ctypes.get_last_error()}")
            continue

        address = ctypes.c_void_p(0)
        while True:
            mbi = MEMORY_BASIC_INFORMATION()
            result = kernel32.VirtualQueryEx(process_handle, address, ctypes.byref(mbi), ctypes.sizeof(mbi))
            if result == 0:
                break

            # 筛选已提交且可读的内存区域
            if mbi.State == MEM_COMMIT and mbi.Protect in (PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY):
                try:
                    # 分配缓冲区,避免过大内存区域导致内存溢出
                    buffer_size = min(mbi.RegionSize, 1024 * 1024)  # 限制单次读取1MB
                    buffer = (ctypes.c_char * buffer_size)()
                    bytes_read = ctypes.c_size_t()

                    if kernel32.ReadProcessMemory(process_handle, mbi.BaseAddress, buffer, buffer_size, ctypes.byref(bytes_read)):
                        # 搜索字符串,处理跨缓冲区的情况
                        buffer_data = buffer[:bytes_read.value]
                        offset = 0
                        while offset <= len(buffer_data) - target_len:
                            if buffer_data[offset:offset+target_len] == target_str:
                                print(f"在进程{pid}中找到'exechack.cc',地址: {hex(mbi.BaseAddress.value + offset)}")
                                # 找到后可选择继续搜索或退出
                                # break
                            offset += 1
                except MemoryError:
                    print(f"读取进程{pid}内存区域{hex(mbi.BaseAddress.value)}时内存不足")

            # 移动到下一个内存区域
            address.value += mbi.RegionSize

        kernel32.CloseHandle(process_handle)

# 示例调用
if __name__ == "__main__":
    find_exechack("target_process.exe")

关键修改说明

  1. 正确定义PROCESSENTRY32结构体:按照Windows API的标准定义结构体字段,确保能正确获取进程ID和进程名。
  2. 放宽内存区域筛选条件:加入所有可读权限的内存类型,避免遗漏存放字符串的只读区域。
  3. 添加API参数类型和返回值定义:明确ctypes函数的参数和返回类型,减少调用错误。
  4. 优化内存读取逻辑:限制单次读取的内存大小(1MB),避免因大内存区域导致内存溢出;同时处理字符串跨缓冲区的情况。
  5. 完善错误处理:对每个系统API调用添加错误码检查,方便调试定位问题。
  6. 适配中文进程名:使用GBK编码解码进程名,避免中文进程名乱码。

内容的提问来源于stack exchange,提问作者Stanislav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 05:17:33