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

Linux x86_64下Shellcode调用dlopen触发SIGSEGV问题求助

问题解决:Shellcode调用dlopen触发SIGSEGV地址边界错误

你要实现无libc依赖的Shellcode加载自定义库,但一直触发SIGSEGV (Address boundary error),核心问题出在指令格式错误、栈对齐、CET指令干扰和执行内存选择这几个点上,下面是具体修复方案:

核心错误分析

  1. call指令的占位符替换逻辑错误:x86-64的call rel32是相对偏移调用,你直接把6字节占位符替换成8字节绝对地址,会破坏指令对齐,导致CPU执行非法指令。
  2. endbr64指令干扰:这个是CET安全机制的指令,在mmap的匿名可执行页中执行时会触发验证失败,直接删掉就行。
  3. 栈未对齐:x86-64要求函数调用前栈必须是16字节对齐,你的代码里sub rsp,16后栈指针没有满足对齐要求,调用dlopen时会出错。
  4. 执行栈上的Shellcode:你最后直接调用栈上的shellcode数组,即使开了-z execstack,栈内存的权限和对齐也不如mmap的匿名页稳定。

分步修复方案

1. 修正call指令为寄存器间接调用

把原来的相对call改成先加载dlopen地址到rax,再call rax,这样可以直接替换绝对地址,不用计算偏移:

  • 原汇编中的call dlopen改成:
    mov rax, 0xDEADBEEFDEADBEEF  ; 8字节占位符
    call rax
    
  • 对应Shellcode中的字节改成:"\x48\xb8XXXXXXXX\xff\xd0"(XXXXXXXX是8字节占位符)

2. 删除endbr64指令

直接删掉汇编中的endbr64语句,以及Shellcode中对应的"\xf3\x0f\x1e\xfa"字节。

3. 强制栈16字节对齐

在进入main后添加and rsp, ~0xf指令,确保栈指针是16字节的倍数:

main:
    push rbp
    mov rbp, rsp
    and rsp, ~0xf  ; 强制栈对齐到16字节
    sub rsp, 16

4. 执行mmap后的可执行内存

把void (*func)() = shellcode;改成void (*func)() = mem;,调用mmap分配的可执行内存,避免栈内存的潜在问题。

5. 修正占位符替换逻辑

原代码中遍历前先自增指针会跳过第一个字节,改成从shellcode起始位置开始遍历,并且用8字节占位符(比如"XXXXXXXX")来匹配,确保替换的是完整的8字节地址:

for (size_t i = 0; i < sizeof(shellcode) - 8; i++) {
    if (memcmp(&shellcode[i], "XXXXXXXX", 8) == 0) {
        memcpy(&shellcode[i], &addrOfDlopen, sizeof(uint64_t));
        break;
    }
}

修复后的完整代码

生成Shellcode用的汇编(main.S)

BITS 64

global _start
section .text

_start:
    call main

main:
    push rbp
    mov rbp, rsp
    and rsp, ~0xf          ; 强制栈16字节对齐
    sub rsp, 16
    mov esi, 2             ; RTLD_NOW | RTLD_GLOBAL
    lea rax, [rel str_lib]
    mov rdi, rax 
    mov rax, 0xDEADBEEFDEADBEEF  ; 8字节占位符
    call rax

    mov eax, 60
    xor edi, edi
    syscall

section .data
str_lib db '/home/nitanmarcel/shell/example/libhello.so', 0

Shellcode加载器C代码

#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <stdlib.h>

int main() {
char shellcode[] =
    "\xe8\x00\x00\x00\x00"              // call main
    "\x55"                              // push rbp
    "\x48\x89\xe5"                      // mov rbp, rsp
    "\x48\x83\xe4\xf0"                  // and rsp, ~0xf
    "\x48\x83\xec\x10"                  // sub rsp, 0x10
    "\xbe\x02\x00\x00\x00"              // mov esi, 2
    "\x48\x8d\x05\x10\x00\x00\x00"      // lea rax, [rip + 0x10]
    "\x48\x89\xc7"                      // mov rdi, rax
    "\x48\xb8"                          // mov rax, 0x0000000000000000
    "XXXXXXXX"                          // 8字节占位符
    "\xff\xd0"                          // call rax
    "\xb8\x3c\x00\x00\x00"              // mov eax, 0x3c
    "\x31\xff"                          // xor edi, edi
    "\x0f\x05"                          // syscall
    "\x00\x2f\x68\x6f\x6d\x65\x2f\x6e"  // 字符串:/home/nitanmarcel/...
    "\x69\x74\x61\x6e\x6d\x61\x72\x63"
    "\x65\x6c\x2f\x73\x68\x65\x6c\x6c"
    "\x2f\x65\x78\x61\x6d\x70\x6c\x65"
    "\x2f\x6c\x69\x62\x68\x65\x6c\x6c"
    "\x6f\x2e\x73\x6f\x00";

    uint64_t addrOfDlopen = (uint64_t)dlopen;
    // 寻找并替换8字节占位符
    for (size_t i = 0; i < sizeof(shellcode) - 8; i++) {
        if (memcmp(&shellcode[i], "XXXXXXXX", 8) == 0) {
            printf("dlopen @%llx\n", addrOfDlopen);
            memcpy(&shellcode[i], &addrOfDlopen, sizeof(uint64_t));
            break;
        }
    }

    void *mem = mmap(NULL, sizeof(shellcode), PROT_READ | PROT_WRITE,
                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (mem == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }

    memcpy(mem, shellcode, sizeof(shellcode));

    if (mprotect(mem, sizeof(shellcode), PROT_READ | PROT_EXEC) == -1) {
        perror("mprotect");
        exit(1);
    }

    void (*func)() = mem;  // 执行mmap后的可执行内存
    func();

    munmap(mem, sizeof(shellcode));

    return 0;
}

编译与运行

编译加载器时需要链接dl库并开启可执行栈:

gcc loader.c -o loader -ldl -z execstack

调试时可以关闭地址随机化:

sudo sysctl -w kernel.randomize_va_space=0

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 18:30:55