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
相关产品推荐
相关产品推荐

