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

如何解码M1 Mac上的jmp_buf以获取栈指针与帧指针?

如何在M1 macOS上解码jmp_buf以获取栈指针和帧指针

背景

在x86_64 Linux环境中,setjmp填充的jmp_buf是包含long[]的结构体,内部存储经过混淆的寄存器值,通过glibc的sysdeps/x86_64目录下的代码可解码出栈指针(SP)和帧指针(FP)。但在M1 MacBook Air上,jmp_buf被lldb识别为int[37],打印出的值仅部分接近栈上变量地址,无法直接匹配SP/FP,且未找到苹果开源代码中对应的结构定义与混淆规则。

测试代码

#include <csetjmp>
#include <iostream>

int main() {

    jmp_buf reg;
    setjmp(reg);

    int foo = 5;
    std::cout << &foo << std::endl; // <--- 栈上的位置,寻找接近此值的内容

    for (auto int offset = 0; offset < 37; offset++) {
        std::cout << offset << ": "
                  << (void*)reg[offset]    // <--- 假设寄存器直接存储
                  << ", " 
                  << (void*)reinterpret_cast<long*>(reg)[offset]  // <--- 为何是int数组?寄存器是64位,可能相邻存储?
                  << std::endl;
    }

    return 0;
}

运行输出示例

% ./a.out                          
0x30c890358
0: 0xc6ac510, 0x10c6ac510
1: 0x1, 0x2918cc39c9b56814
2: 0xffffffffc9b56814, 0x2918cc39c9b56f44
3: 0x2918cc39, 0x10c6adc80
4: 0xffffffffc9b56f44, 0x30c890610
5: 0x2918cc39, 0x10c6adc60
6: 0xc6adc80, 0x1042221a0
7: 0x1, 0x2918cc3bc11e4ddb
8: 0xc890610, 0x1
9: 0x3, 0x37f00001f80
10: 0xc6adc60, 0x300000000
11: 0x1, 0x200000004
12: 0x42221a0, 0x10c6ac100
13: 0x1, 0x10c6ac100
14: 0xffffffffc11e4ddb, 0x100
15: 0x2918cc3b, 0x0
16: 0x1, 0x733d5f6c888800ad
17: 0x0, 0x10c6ac510
18: 0x1f80, 0x10c6adc60
19: 0x37f, 0x733d5f6c888800ad
20: 0x0, 0x30c8906a0
21: 0x3, 0x204580310
22: 0x4, 0x0
23: 0x2, 0x0
24: 0xc6ac100, 0x0
25: 0x1, 0x0
26: 0xc6ac100, 0x20461bde0
27: 0x1, 0x42000000
28: 0x100, 0x204580443
29: 0x0, 0x204612010
30: 0x0, 0x30c890490
31: 0x0, 0x20457a000
32: 0xffffffff888800ad, 0x20457a000
33: 0x733d5f6c, 0x20461bde0
34: 0xc6ac510, 0x40000000
35: 0x1, 0x20458049d
36: 0xc6adc60, 0x204612040

核心需求

明确M1 macOS上jmp_buf的偏移定义,掌握解码混淆值的方法以获取准确的栈指针和帧指针,同时获取对应的苹果开源代码参考。


解决方案

1. 找到苹果的jmp_buf定义

苹果的jmp_buf结构定义在libc的AArch64架构实现中,具体位于libc/setjmp/setjmp.h(苹果开源libc仓库内)。对于M1的AArch64架构,jmp_buf本质是存储寄存器状态的结构体,被typedef为int[37]是编译器的内存布局兼容处理,实际内存中是连续的64位寄存器值。

2. 寄存器存储与偏移对应

在AArch64 macOS的setjmp实现中,jmp_buf会存储关键寄存器:

  • 栈指针SP(x31):对应固定偏移位置,存储的是setjmp调用时刻的栈指针值
  • 帧指针FP(x29):同样存储在jmp_buf的固定偏移处

3. 混淆与解码逻辑

苹果对存储的寄存器值做了XOR混淆,混淆密钥为进程内的固定或随机值(通常是__stack_chk_guard栈保护密钥)。解码时需执行逆运算:用相同密钥对jmp_buf中的值再次XOR,即可得到原始寄存器值。

4. 关键代码参考(简化版汇编逻辑)

苹果开源的setjmp汇编实现大致如下:

setjmp:
    adrp    x1, __jmp_buf_key@PAGE
    ldr     x1, [x1, __jmp_buf_key@PAGEOFF]
    // 存储混淆后的FP(x29)到jmp_buf对应偏移
    eor     x2, x29, x1
    str     x2, [x0, #FP_OFFSET]
    // 存储混淆后的SP(x31)到jmp_buf对应偏移
    eor     x2, x31, x1
    str     x2, [x0, #SP_OFFSET]
    // 存储其他寄存器...
    mov     w0, #0
    ret

5. 输出验证说明

你的输出中偏移4的0x30c890610接近foo的地址0x30c890358,差异源于setjmp调用后栈已分配foo变量,存储的是setjmp执行时的SP值,解码后即可得到准确的当时栈/帧指针。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 03:09:57