macOS下C语言DYLIB调用CheatEngine指针读取内存值异常求助
问题分析与解决方案
你的核心问题是错误地将目标程序的Mach-O头部地址当成了__DATA段的基地址,导致偏移计算错误,读取到垃圾内存值。
错误根源
CheatEngine中显示的dyld.DATA+000002A0,这里的dyld.DATA指的是目标进程(main)的__DATA段的起始地址,而你的get_base_address函数返回的是main程序的Mach-O文件头部地址(通过_dyld_get_image_header获取),这两个地址完全不同。直接用头部地址加偏移,自然会指向错误的内存区域。
另外补充:你的main程序中money是栈上的局部变量,正常情况下不会出现在__DATA段,推测你可能关闭了ASLR或者编译时做了特殊优化,让变量被放在全局数据区,但这不影响当前的偏移计算问题。
修正步骤
- 获取目标程序的__DATA段基地址:遍历Mach-O的加载命令,找到
__DATA段的虚拟内存地址。 - 用__DATA段基地址加上正确偏移:替换原代码中用头部地址加偏移的逻辑。
修改后的Hack.c代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <mach-o/dyld.h> #include <mach/mach.h> #include <mach/vm_map.h> #include <unistd.h> #include <pthread.h> #include <mach-o/loader.h> // 获取目标程序的__DATA段基地址 void *get_data_segment_base() { uint32_t count = _dyld_image_count(); for (uint32_t i = 0; i < count; i++) { const char *name = _dyld_get_image_name(i); if (name && strstr(name, "main") != NULL) { const struct mach_header_64 *header = (const struct mach_header_64 *)_dyld_get_image_header(i); if (!header) continue; // 遍历加载命令,找到__DATA段 const struct load_command *cmd = (const struct load_command *)((uintptr_t)header + sizeof(struct mach_header_64)); for (uint32_t j = 0; j < header->ncmds; j++) { if (cmd->cmd == LC_SEGMENT_64) { const struct segment_command_64 *seg_cmd = (const struct segment_command_64 *)cmd; if (strcmp(seg_cmd->segname, "__DATA") == 0) { printf("__DATA segment base found: %p\n", (void *)seg_cmd->vmaddr); return (void *)seg_cmd->vmaddr; } } cmd = (const struct load_command *)((uintptr_t)cmd + cmd->cmdsize); } } } return NULL; } // 读取/修改money值的函数 void write_money(int new_value) { void *data_base = get_data_segment_base(); if (data_base) { unsigned int offset = 0x2A0; // CheatEngine找到的偏移 void *money_address = (void *)((uintptr_t)data_base + offset); // 读取当前值 int current_value = *(int *)money_address; printf("Current money value at %p: %d\n", money_address, current_value); // 修改值(取消注释启用) // *(int *)money_address = new_value; // printf("Money value changed to: %d\n", new_value); } else { printf("Failed to locate __DATA segment base\n"); } } // 线程函数 void *hack_thread(void *arg) { sleep(1); while (1) { void *data_base = get_data_segment_base(); if (data_base) { printf("Main program is running, ready to inject hack!\n"); break; } printf("Waiting for main program to load...\n"); sleep(1); } write_money(99999); return NULL; } __attribute__((constructor)) void hack_init() { printf("Hack loaded!\n"); pthread_t thread_id; if (pthread_create(&thread_id, NULL, hack_thread, NULL) != 0) { printf("Failed to create hack thread\n"); return; } pthread_detach(thread_id); }
额外注意事项
- 如果
money是栈上的局部变量,即使修正了偏移,跨运行时地址可能会变化(因为ASLR和栈布局的随机性),你之前用CheatEngine找到的偏移可能只对当前运行实例有效。若要稳定获取栈变量,需要通过hook函数(比如hookfgets)来定位栈帧中的money地址。 - 编译时确保目标程序和DYLIB的架构一致(都是x86_64或arm64)。
内容的提问来源于stack exchange,提问作者Muhammad Ali
相关产品推荐
相关产品推荐

