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

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或者编译时做了特殊优化,让变量被放在全局数据区,但这不影响当前的偏移计算问题。

修正步骤

  1. 获取目标程序的__DATA段基地址:遍历Mach-O的加载命令,找到__DATA段的虚拟内存地址。
  2. 用__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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 20:42:18