通过ptrace设置寄存器调用dlopen触发SIGSEGV问题(Intel 64)
问题原因分析
核心问题出在dlopen的复杂执行逻辑破坏了跳板代码的返回环境或内存完整性——malloc这类简单函数不会触发这个问题,具体拆解为以下几点:
跳板内存被动态链接器覆盖
如果你的跳板代码写在栈空间、或者ld-linux-x86-64.so.2会复用的临时内存段里,dlopen执行_dl_map_object_from_fd这类加载逻辑时,会大量操作内存(包括栈、临时缓冲区),直接把跳板里callq *%r8后面的int $3指令覆盖了。dlopen执行完返回时,rip指向的是被覆盖的垃圾地址,自然触发SIGSEGV。而malloc只做内存分配,不会碰这些敏感区域。栈上下文被彻底破坏
dlopen内部有一堆嵌套调用(比如_dl_map_object_from_fd、_dl_load_cache_lookup),会深度修改栈帧和rsp寄存器的值。你靠ptrace搭的跳板返回逻辑(callq返回后执行int3)完全依赖原栈的完整性,但dlopen跑完栈已经乱了,callq的返回地址(跳板的int3位置)根本找不到,rip就跑到了ld-linux的open_path这类非法区域,直接崩了。malloc的栈操作简单,不会搞乱原栈的返回上下文。跳板内存页权限被修改
要是跳板所在的内存页,在dlopen加载库时被动态链接器改了权限(比如从可执行改成不可执行),那dlopen返回后执行int $3时,就会触发权限错误导致SIGSEGV。malloc执行过程不会改这类内存页的权限。
验证&修复方向
- 触发SIGSEGV后,用ptrace读跳板地址的内存,看看
int $3还在不在,同时查该内存页的权限是否还是可执行。 - 换个安全的内存区域放跳板:比如先调用malloc申请一块可写可执行的堆内存,或者找进程里本来就存在的可写可执行段,避免被动态链接器覆盖。
内容的提问来源于stack exchange,提问作者SimpleCoder
相关产品推荐
相关产品推荐

