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

macOS下模拟Cheat Engine:获取进程内存并修改的后续实现问题

在macOS上实现Cheat Engine式内存扫描与修改

你已经成功获取到目标进程的PID和对应的mach_port_t,接下来可以通过mach_vm系列API完成内存枚举、扫描、修改三个核心操作,以下是具体实现方案:

核心步骤与代码实现

1. 枚举进程可读写内存区域

首先需要遍历目标进程的所有内存区域,筛选出可读写的用户内存区域(游戏数值通常存储在这类区域):

#include <mach/mach_vm.h>

void list_memory_regions(mach_port_t target_task) {
    mach_vm_address_t address = 0;
    mach_vm_size_t size;
    vm_region_basic_info_data_64_t info;
    mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
    mach_port_t object_name;

    while (mach_vm_region(target_task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &info_count, &object_name) == KERN_SUCCESS) {
        // 筛选可读写、非系统子映射的区域
        if ((info.protection & VM_PROT_READ) && (info.protection & VM_PROT_WRITE) && !(info.reserved & VM_REGION_SUBMAP)) {
            printf("内存区域: 0x%llx - 0x%llx, 大小: %llu\n", address, address + size, size);
        }
        address += size;
        mach_port_deallocate(mach_task_self(), object_name);
    }
}

2. 扫描内存中的目标数值

对筛选出的内存区域,逐段读取并比对目标数值(以32位整数为例):

typedef struct {
    mach_vm_address_t address;
    uint32_t value;
} MatchResult;

MatchResult* scan_memory_for_uint32(mach_port_t target_task, uint32_t target_value, uint32_t* match_count) {
    mach_vm_address_t address = 0;
    mach_vm_size_t size;
    vm_region_basic_info_data_64_t info;
    mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
    mach_port_t object_name;
    MatchResult* results = NULL;
    *match_count = 0;

    while (mach_vm_region(target_task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &info_count, &object_name) == KERN_SUCCESS) {
        if ((info.protection & VM_PROT_READ) && !(info.reserved & VM_REGION_SUBMAP)) {
            uint32_t* buffer = malloc(size);
            if (!buffer) {
                address += size;
                mach_port_deallocate(mach_task_self(), object_name);
                continue;
            }

            mach_vm_size_t bytes_read;
            kern_return_t ret = mach_vm_read_overwrite(target_task, address, size, (mach_vm_address_t)buffer, &bytes_read);
            if (ret == KERN_SUCCESS) {
                uint32_t num_elements = bytes_read / sizeof(uint32_t);
                for (uint32_t i = 0; i < num_elements; i++) {
                    if (buffer[i] == target_value) {
                        results = realloc(results, sizeof(MatchResult) * (*match_count + 1));
                        results[*match_count].address = address + (i * sizeof(uint32_t));
                        results[*match_count].value = target_value;
                        (*match_count)++;
                    }
                }
            }
            free(buffer);
        }
        address += size;
        mach_port_deallocate(mach_task_self(), object_name);
    }
    return results;
}

3. 修改目标内存地址的数值

找到匹配地址后,通过mach_vm_write修改对应内存的值:

kern_return_t write_uint32_to_memory(mach_port_t target_task, mach_vm_address_t address, uint32_t new_value) {
    return mach_vm_write(target_task, address, (vm_offset_t)&new_value, sizeof(new_value));
}

4. 整合到现有代码

在你的main函数中,拿到port后即可调用上述函数:

int main() {
    // ... 你的现有代码,获取pid和port ...

    if (port != 0) {
        // 可选:列出内存区域用于调试
        list_memory_regions(port);

        // 扫描数值(示例:查找值为100的32位整数)
        uint32_t match_count;
        MatchResult* matches = scan_memory_for_uint32(port, 100, &match_count);
        printf("找到 %u 个匹配地址\n", match_count);
        
        // 遍历匹配结果并修改
        for (uint32_t i = 0; i < match_count; i++) {
            printf("地址: 0x%llx, 当前值: %u\n", matches[i].address, matches[i].value);
            kern_return_t ret = write_uint32_to_memory(port, matches[i].address, 999);
            if (ret == KERN_SUCCESS) {
                printf("修改为999成功\n");
            } else {
                printf("修改失败,错误码: %d\n", ret);
            }
        }
        free(matches);
    }

    return 0;
}

关键注意事项

  • 权限要求:必须以root权限运行程序(sudo ./your_program),否则task_for_pid及后续mach_vm操作会失败。
  • 64位兼容性:macOS为64位系统,必须使用mach_vm_*系列函数(而非老旧的vm_*函数),地址类型用mach_vm_address_t(64位)。
  • 数据类型适配:如果需要扫描浮点数、64位整数等其他类型,只需修改缓冲区类型和比对逻辑即可。
  • 错误处理:每个mach_vm函数都会返回kern_return_t错误码,需做好异常判断(如内存读取失败时跳过对应区域)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 02:15:39