C语言实现上下文切换时替代混淆jmp_buf的可行方案问询
针对Linux jmp_buf指针混淆的替代实现方案
你给出的i64_ptr_mangle是x86_64架构下glibc用于混淆jmp_buf中敏感指针的标准实现,逻辑固定为「指针异或TLS段中的canary值(fs:0x30)+ 循环左移17位」,针对你的基础线程库开发需求,有3种可行替代方案:
方案1:实现配对的解混淆/重混淆函数
不需要替换现有逻辑,直接根据加密逻辑反向实现解密函数,修改完指针后再重新加密写回jmp_buf即可,代码如下:
// 原有加密函数,用于修改后重新混淆指针 static long int i64_ptr_mangle(long int p) { long int ret; asm(" mov %1, %%rax;\n" " xor %%fs:0x30, %%rax;" " rol $0x11, %%rax;" " mov %%rax, %0;" : "=r"(ret) : "r"(p) : "%rax" ); return ret; } // 新增解密函数,用于读取jmp_buf中的原始指针 static long int i64_ptr_demangle(long int p) { long int ret; asm(" mov %1, %%rax;\n" " ror $0x11, %%rax;" // 反向操作:先循环右移17位 " xor %%fs:0x30, %%rax;" // 再异或canary值 " mov %%rax, %0;" : "=r"(ret) : "r"(p) : "%rax" ); return ret; }
操作逻辑:
- 调用系统
setjmp保存上下文后,用i64_ptr_demangle把jmp_buf中存储的rsp、rbp、rip字段解密得到原始值 - 修改对应指针为你线程的自定义栈地址、入口地址后,用
i64_ptr_mangle重新混淆后写回jmp_buf对应位置 - 后续调用
longjmp即可正常跳转到自定义上下文执行
方案2:完全自定义轻量上下文保存逻辑,绕开系统jmp_buf
如果你不想依赖glibc的setjmp实现逻辑,避免不同版本glibc混淆规则变动的问题,可以直接用汇编实现极简的上下文保存/恢复逻辑,只保存你需要的寄存器,完全不需要处理混淆:
# 自定义my_setjmp实现,x86_64架构 .global my_setjmp my_setjmp: mov %rbx, (%rdi) mov %rbp, 8(%rdi) mov %r12, 16(%rdi) mov %r13, 24(%rdi) mov %r14, 32(%rdi) mov %r15, 40(%rdi) mov %rsp, 48(%rdi) # 保存返回地址作为rip mov (%rsp), %rax mov %rax, 56(%rdi) xor %rax, %rax ret # 自定义my_longjmp实现 .global my_longjmp my_longjmp: mov (%rdi), %rbx mov 8(%rdi), %rbp mov 16(%rdi), %r12 mov 24(%rdi), %r13 mov 32(%rdi), %r14 mov 40(%rdi), %r15 mov 48(%rdi), %rsp mov 56(%rdi), %rax mov %rax, (%rsp) mov %rsi, %rax ret
对应的C头文件定义:
// 自定义上下文结构,无混淆,直接访问字段修改即可 typedef struct { long rbx; long rbp; long r12; long r13; long r14; long r15; long rsp; long rip; } my_jmp_buf; int my_setjmp(my_jmp_buf *env); void my_longjmp(my_jmp_buf *env, int val);
这个方案完全不依赖系统的setjmp.h和ucontext.h,你可以直接修改结构体里的rsp、rbp、rip字段,不需要任何加解密操作,适合线程库底层开发。
方案3:编译期关闭指针混淆(仅调试用)
如果是开发阶段临时调试用,可以在编译glibc的时候关闭PTR_MANGLE配置选项,或者给你的程序编译时加上-fno-stack-protector参数(部分发行版会同时关闭指针混淆逻辑),但这个方案不推荐生产环境使用,会降低安全性。
内容的提问来源于stack exchange,提问作者Gaurav Chaudhari
相关产品推荐
相关产品推荐

