首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
逆向工程
发新帖
3
0
[原创]一种基于 Rosetta2 二进制翻译的 Syscall 拦截方法
发表于: 2026-1-28 01:35
2561
[原创]一种基于 Rosetta2 二进制翻译的 Syscall 拦截方法
EX呵呵
2026-1-28 01:35
2561
# 一种基于 Rosetta2 二进制翻译的 Syscall 拦截方法 ## 目录 - [一种基于 Rosetta2 二进制翻译的 Syscall 拦截方法](#一种基于-rosetta2-二进制翻译的-syscall-拦截方法) - [目录](#目录) - [引言](#引言) - [技术路线](#技术路线) - [传统方案](#传统方案) - [Rosetta2 拦截](#rosetta2-拦截) - [Rosetta2 二进制翻译原理](#rosetta2-二进制翻译原理) - [oahd / oahd-helper](#oahd--oahd-helper) - [runtime](#runtime) - [libRosettaAot / libRosettaRuntime](#librosettaaot--librosettaruntime) - [拦截思路](#拦截思路) - [鸣谢](#鸣谢) ## 引言 随着 Wine 等兼容技术的持续发展,大量 Windows 游戏与应用程序已能流畅运行于 Linux 平台。然而,将这一生态迁移至 MacOS 时仍面临显著障碍。 由于 MacOS 内核缺失 Seccomp 机制,Wine 无法对进程的系统调用进行内核级拦截。这导致那些绕过 Windows API 直接执行 Syscall 指令的程序在 MacOS 上触发错误的系统调用号,最终引发崩溃或行为异常。 针对这一平台差异,本文提出了一种创新的解决方案,即利用 Apple Silicon 架构下的 Rosetta2 二进制翻译器,在指令转换层面实现对 x86 系统调用的捕获与重定向,从而在用户态构建起类 Seccomp 的拦截能力。 ## 技术路线 ### 传统方案 在 Linux 平台上,Wine 依赖 BPF 在内核态设立"陷阱门"。当进程执行 Syscall 指令时,内核先检查过滤器规则,若为 Windows 调用则返回用户态交由 Wine 模拟。 然而,MacOS 的 XNU 内核先天缺失 Seccomp 机制,且出于安全策略禁止第三方内核扩展介入系统调用路径。 Wine 自身的用户态 Hook 仅能覆盖通过 ntdll.dll 导出的接口,对于直接内联汇编执行 Syscall 的指令流无能为力。这类指令会直接陷入内核,触发错误的系统调用号,导致程序行为出现异常。 ### Rosetta2 拦截 Apple Silicon 架构下的 Rosetta2 作为 x86_64 到 ARM64 的动态二进制翻译器,强制所有 x86 指令经过其翻译。 这一架构特性创造了独特的干预窗口:无论原始 x86 代码如何编写,都必须经过 Rosetta2 的解码、翻译,最终生成 ARM64 代码才能执行。 并且 Rosetta2 在用户态运行,其内部数据结构、乃至生成的本地代码均处于用户态可访问的内存空间。 这为我们提供了在不触碰内核的前提下,修改其内部流程以拦截 Syscall。 ## Rosetta2 二进制翻译原理 Rosetta2 其大致分为三个部分包括 `oahd / oahd-helper`、`runtime`、`libRosettaAot / libRosettaRuntime` ### oahd / oahd-helper 其中`oahd`为 Rosetta2 守护进程,其负责管理与创建 AOT 文件。 在启动 x86 程序时,翻译引擎会通过 XPC 向`oahd`请求 AOT 文件,此时`oahd`会检查`/var/db/oah/*`目录下是否存在对应程序的 AOT 文件,若 AOT 不存在,其会调用`oahd-helper`并传入文件句柄以创建对应的 AOT 文件。 `oahd-helper`中 AOT 流程大致分为以下几个步骤 读取 Mach-O 文件 -> 判断文件类型 -> 解析区段等信息 -> 转换为`rosetta::librosetta_aot::ModuleResult` -> `rosetta::librosetta_aot::translate`识别函数翻译并构建映射 -> 获取翻译后的 Data -> 应用各种 Fixup (如 runtime routines ) -> 构建新的 Mach-O 文件 -> 映射符号 -> 签名输出 其对程序进行了静态分析并调用了`libRosettaAot`进行 AOT ### runtime `runtime`为 Rosetta2 的运行时代码,在启动 x86 程序时系统首先加载`runtime`,再有`runtime`向`oahd`请求 AOT 文件。 `runtime`在请求到 AOT 文件后会解析验证 AOT 文件与映射信息并将其加载到内存之中,并移交控制权到 AOT 代码。 `runtime` 也大致由三个部分构成,分别是`AOT Loader`、`JIT Engine`、`Runtime Routine`。 其中`AOT Loader`用于验证签名并加载之前所属的 AOT 文件,`JIT Engine`则用于动态解码翻译某些之前静态分析所漏掉的函数,如:动态链接库中的函数、ShellCode、由于不透明谓词未能分析到的函数。 `Runtime Routine`则用于为翻译后的代码提供功能支持,其中 Syscall 其会在翻译过程中被转换为`call indirect_jmp_dyld_stub`类似的代码,Syscall 的功能将由`Runtime Routine`提供,其将 x86 的调用转换为符合当前系统的调用以模拟 Syscall。 此外`JIT Engine`也由`Runtime Routine`触发,其辅助函数中包含`indirect_jmp_dyld_stub`等触发函数,在第一次调用某个动态链接函数时,会通过此辅助函数获取欲调用函数翻译后的地址。 此函数调用后`runtime`将会从代码映射红黑树中搜索欲调用的函数是否被翻译(AOT 中文件中的映射也将被加载),若目标函数未被翻译(即未被 AOT 或首次调用),`runtime`则会调用`libRosettaRuntime`的`rosetta::runtime::library::ir_create`函数进行翻译(与 AOT 不同这里使用的另一个翻译函数,而且这里的`libRosettaRuntime`也是专门经过调整的翻译引擎),翻译后指令将被`JIT Engine`加载到内存之中。 `JIT Engine`辅助函数(`Runtime Routine`)大致流程: ```cpp uint64_t ThreadContext::cache_slot_miss(Context context, uint64_t x86_address, uint64_t stubs_sh_address) { Translator translator = context->translator; uint32_t type = context->type; uint64_t arm_addrress; // 寻找已翻译的函数中是否包含当前欲调用函数 if (find_translation_in_tree_x86(translator, x86_address, &arm_addrress, 0, stubs_sh_address, type)) { return arm_addrress; } else { // 如果没有找到就现场进行翻译,并将地址插入红黑树 return jit_translation_for_x86_address(translator, x86_address, 0, 0, stubs_sh_address, type); } } uint64_t indirect_jmp_dyld_stub@<X0>( uint64_t context@<x18>, uint64_t x86_address@<x22>, uint64_t stubs_sh_address@<x23> ) { context->save_context(); context->cache_slot_miss(context, x86_address, stubs_sh_address); runtime_restore() } ``` Syscall 辅助函数(`Runtime Routine`)大致流程: ```cpp // 此处模拟了 x86 架构 Syscall 指令的行为与传参,具体请参照寄存器映射表。 uint64_t __usercall syscall_handler@<X0>( uint32_t svc_code@<W0>, uint64_t rcx@<X1>, uint64_t rdx@<X2>, uint64_t rbx@<X3>, uint64_t rsp@<X4>, uint64_t rbp@<X5>, uint64_t rsi@<X6>, uint64_t rdi@<X7>, uint64_t r8@<X8>) { switch(svc_code) { // 调用转换具体实现 } } ``` 目前已知的辅助函数: ```text 说明: 辅助函数偏移量存储于`runtime_pointers`节,顺序与名字一致。 在运行时`runtime`会使用`register_runtime_routine_offsets`函数将辅助函数注册到翻译引擎。 随后翻译引擎将会通过之前记录的 Fixup 地址填充实际的辅助函数地址。 "branch_slot" "indirect_jmp" // JIT 间接跳转 "indirect_jmp_dyld_stub" // JIT 动态链接函数 "far_jmp" "indirect_call" // JIT 间接调用 "far_call" "return_stack_miss" "far_ret" "far_ret_single_step" "syscall_handler" // Syscall 转换 "toggle_single_step" "cpuid" // 模拟 Cpuid "wide_udiv_64" "wide_sdiv_64" "pcmpestri" "pcmpestrm" "pcmpistri" "pcmpistrm" "mov_segment" "mov_segment_and_reg" "get_cpu_number" "get_tls_base" // 模拟 TLS "read_timer_nanoseconds" "read_mxcsr" // x87 系列指令模拟 "mxcsr_to_fpcr_fpsr" "f2xm1" "fabs" "fadd_f32" "fadd_f64" "fadd_ST" "fbld" "fbstp" "fchs" "fcmov" "fcom_f32" "fcom_f64" "fcom_ST" "fcomi" "fcos" "fdecstp" "fdiv_f32" "fdiv_f64" "fdiv_ST" "fdivr_f32" "fdivr_f64" "fdivr_ST" "fiadd" "ficom" "fidiv" "fidivr" "fild" "fimul" "fincstp" "fist_i16" "fist_i32" "fist_i64" "fistt_i16" "fistt_i32" "fistt_i64" "fisub" "fisubr" "fld_fp32" "fld_fp64" "fld_fp80" "fld_constant" "fld_STi" "fmul_f32" "fmul_f64" "fmul_ST" "fpatan" "fprem" "fprem1" "fptan" "frndint" "fscale" "fsin" "fsincos" "fsqrt" "fst_fp32" "fst_fp64" "fst_fp80" "fst_STi" "fsub_f32" "fsub_f64" "fsub_ST" "fsubr_f32" "fsubr_f64" "fsubr_ST" "fucom" "fucomi" "fxam" "fxch" "fxtract" "fyl2x" "fyl2xp1" "ffree" "load_segment_limit" "rdrand" "xrstor" "xsave" ``` ### libRosettaAot / libRosettaRuntime `libRosettaAot / libRosettaRuntime` 为核心翻译引擎,其中包含了控制流提取,基本块/指令翻译,映射/重定位等多个功能。 注: `libRosettaRuntime`是经过严格地址布局过的运行时专用版翻译引擎,由于运行时环境不包含`dyld`无法动态链接所以需要单独制作一个运行时特化版翻译引擎。 根据其内部残留字符串可知其支持 x86 的下列指令集: ```text Translator.cpp 普通 x86 指令集 TranslatorAes.cpp x86 aes 加速指令集 TranslatorArithmetic.cpp TranslatorBitTest.cpp TranslatorFlags.cpp TranslatorFlagsSaveRestore.cpp TranslatorHelpers.cpp TranslatorMulDiv.cpp TranslatorShifts.cpp TranslatorSseConversions.cpp x86 sse 加速指令集 TranslatorSseFpComparisons.cpp TranslatorSseMovesShuffles.cpp TranslatorSseShifts.cpp TranslatorSseStringOps.cpp TranslatorStringOps.cpp TranslatorX87.cpp x87 浮点数指令集 ``` 其内部寄存器映射(来自<a href="elink@fc9K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3k6Y4u0A6i4K6u0W2k6$3W2@1K9s2g2T1i4K6u0W2K9h3!0Q4x3V1k6b7M7X3!0B7k6h3y4@1b7$3S2S2L8i4m8G2L8r3I4A6L8$3&6Q4x3V1j5`.">ProjectChampollion</a>): | x86_64 | arm64 | |------------|----------| | RAX | x0 | | RCX | x1 | | RDX | x2 | | RBX | x3 | | RSP | x4 | | RBP | x5 | | RSI | x6 | | RDI | x7 | | R8 | x8 | | R9 | x9 | | R10 | x10 | | R11 | x11 | | R12 | x12 | | R13 | x13 | | R14 | x14 | | R15 | x15 | | XMM0~15 | q0~15 | ## 拦截思路 基于以上翻译原理我们可以使用以下思路对特定地址或特定调用号的 Syscall 进行拦截。 首先我们将通过特征码搜索`syscall_handler`与`ThreadContext::cache_slot_miss`,获取到函数地址后分别在两个函数中放置 Hook。 此后通过`cache_slot_miss`中的 Hook 恢复 ARM 与 x86 地址的映射关系,并将欲拦截函数放入一个全局的黑名单。 最后在`syscall_handler`中通过返回地址识别欲拦截函数并通过信号将 Context 发送至 Wine 层。 注:MacOS 对 Rosetta2 代码进行了代码签名以及修改检查等保护,可使用 Debugger 的思路来绕过 MacOS 对 Rosetta2 层代码的保护,具体方法请参考<a href="elink@92aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6i4K9h3&6W2b7h3&6V1b7i4q4#2j5g2)9J5c8Y4u0G2M7$3g2@1N6r3q4^5z5o6M7`.">rosettax87</a> ## 鸣谢 <a href="elink@93dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3k6Y4u0A6i4K6u0W2k6$3W2@1K9s2g2T1i4K6u0W2K9h3!0Q4x3V1k6b7M7X3!0B7k6h3y4@1b7$3S2S2L8i4m8G2L8r3I4A6L8$3^5`.">ProjectChampollion</a> <a href="elink@fddK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6i4K9h3&6W2b7h3&6V1b7i4q4#2j5g2)9J5c8Y4u0G2M7$3g2@1N6r3q4^5z5o6M7`.">rosettax87</a>
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
最后于
2026-2-1 00:10 被EX呵呵编辑 ,原因: 修改正确的函数名
#调试逆向
#系统底层
收藏
・
3
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
2
)
Imxz
雪 币:
112
活跃值:
(9435)
能力值:
( LV2,RANK:10 )
在线值:
发帖
6
回帖
754
粉丝
9
关注
私信
Imxz
2
楼
tql
2026-1-28 15:01
0
咖啡_741298
雪 币:
4
活跃值:
(7880)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
162
粉丝
4
关注
私信
咖啡_741298
3
楼
直接内联汇编执行 Syscall 的程序,没必要兼容了吧, 但凡有这种操作的,100%是垃圾程序不值得去兼容
2026-2-2 18:33
1
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
EX呵呵
8
发帖
245
回帖
25
RANK
关注
私信
他的文章
[原创]一种基于 Rosetta2 二进制翻译的 Syscall 拦截方法
2561
[原创]MSVC编译器/Warbird初步逆向记录
12693
[原创]Steins Gate “命运石之门” 一种基于veh的r3拦截syscall的方法
3551
[分享]关于某知名游戏公司被挂马的文件分析
12283
[求助]求大佬帮忙看个算法
5282
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部