汇编和C混合:重新分配RSP寄存器导致分段错误

汇编和C混合:重新分配RSP寄存器导致分段错误,c,gcc,assembly,x86-64,inline-assembly,C,Gcc,Assembly,X86 64,Inline Assembly,我试图为一个子函数重新分配堆栈。我试图通过在Linux x86_64体系结构上混合汇编和C来实现这一点 以下是我的计划: 1.通过mmapsyscall获取一段内存。 2.将映射内存传递给asm func,然后将其分配给RSP寄存器并跳转到该函数 C功能: typedef struct Context { uint64_t rbx; // 0 uint64_t rsp; // 8 uint64_t rbp; // 16 uint

我试图为一个子函数重新分配堆栈。我试图通过在Linux x86_64体系结构上混合汇编和C来实现这一点

以下是我的计划: 1.通过
mmap
syscall获取一段内存。 2.将映射内存传递给asm func,然后将其分配给RSP寄存器并跳转到该函数

C功能:

typedef struct Context
{
    uint64_t    rbx;    // 0
    uint64_t    rsp;    // 8
    uint64_t    rbp;    // 16
    uint64_t    r12;    // 24
    uint64_t    r13;    // 32
    uint64_t    r14;    // 40
    uint64_t    r15;    // 48
    uint64_t    rip;    // 56
    uint64_t    break_point;    // 64
} Context_st;

main_func()
{
    ...
    Context_st contexts[1];
    Context_st mainContexts;

    // inittialize main stack and the function's stack. This should make the func return to main function, which is this one.
    _init_contexts(contexts);

    // This should backup main function's context and jump to another function with customized stack
    _run_contexts(&mainContexts, contexts);

    printf("Successfully returned\n");       // This could not be executed.

    ...
}

static void _init_contexts(Context_st pContexts[1])
{
    int mmapFlags = MAP_PRIVATE | MAP_ANONYMOUS;
    const size_t STACK_SIZE = 512;

    asm_init_context(&(pContexts[0]));
    pContexts[0].rip = (uint64_t)_some_func;
    pContexts[0].break_point = (uint64_t)_func_to_go;
    pContexts[0].rsp = (uint64_t)mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, mmapFlags, -1, 0);
    pContexts[0].rsp += STACK_SIZE;

    return;
}

static void _run_contexts(Context_st *pMainCtx, Context_st *pSubCtx)
{
    asm_save_main_and_go(pMainCtx, pSubCtx);
    return;
}
和装配功能:

#################################################
##
    .text
    .align 4
    .globl asm_init_context
    .type asm_init_context, @function
asm_init_context:
    movq %rbx, (%rdi)
    movq %rsp, 8(%rdi)
    movq %rbp, 16(%rdi)
    movq %r12, 24(%rdi)
    movq %r13, 32(%rdi)
    movq %r14, 40(%rdi)
    movq %r15, 48(%rdi)
    retq

#################################################
##
    .text
    .align 4
    .globl asm_save_main_and_go
    .type asm_save_main_and_go, @function
asm_save_main_and_go:

    movq %rbx, (%rdi)
    movq %rsp, 8(%rdi)
    movq %rbp, 16(%rdi)
    movq %r12, 24(%rdi)
    movq %r13, 32(%rdi)
    movq %r14, 40(%rdi)
    movq %r15, 48(%rdi)

    movl %esi, %eax
    movq (%rsi), %rbx
    movq 8(%rsi), %rsp      # This is the code which causes SIGSEGV
    movq 16(%rsi), %rbp
    movq 24(%rsi), %r12
    movq 32(%rsi), %r13
    movq 40(%rsi), %r14
    movq 48(%rsi), %r15
    movq 56(%rsi), %rdx
    push %rdx
    jmp 64(%rsi)
我已经标记了导致SIGSEGV的asm代码。如果我把它注释掉,程序可能会正确运行并返回shell。但我不知道为什么

我做错什么了吗?或者如果我需要做一些额外的事情,使mmapped内存部分能够作为定制的堆栈空间用于RSP寄存器

非常感谢您


11月22日更新:

这很奇怪:当我切换到另一个虚拟机时,成功地调用了_func_to_go()。虽然返回后还有另一个分段错误,但由于错误堆栈,可能是已知问题

如果复制问题,我将不断更新。但我希望这仍然是“工作模式”


特别感谢和。您的讨论指出了我的一些错误,我已经或将在我的项目中纠正它们。:-)

我要求您做的第一件事是检查
mmap
是否成功。它可能失败了,您可能正试图读取/写入不可访问的内存。最后,您需要从
asm\u save\u main\u和\u go
附加一个调试器和单步,并检查它到底在哪里失败。@AjayBrahmakshatriya如果
movq 8(%rsi),%rsp
真的是SIGSEGV,那就不能是对齐问题,只需将
mov
转换为
rsp
即可。对齐将在以后引起问题。所以你关于无效内存的观点更有可能。但是我希望它已经在
movq(%rsi),%rbx
.Oh和
pContexts[0]崩溃了mmap
堆栈大小
。虽然它在技术上是正确的,但如果堆栈完全为空,
[rsp]
仍然被视为“堆栈顶部值”,并且仅出于某些检查目的而达到该值的任何代码都将到达
mmap
区域之外,我宁愿将一些
nullptr
或“canary”值放入新堆栈中,因此,它将指向
stack\u region.end()-8
地址,而不是确切地指向
end()
@Ped7g。我不认为是该指令导致了问题。好吧,没有理由指令会失败。就像OP提到的那样,他通过观察代码在没有指令的情况下运行良好来挑出指令。所以这一定是因为这个指令而发生的事情。这里的不对准肯定会在libc中的某个地方导致崩溃。我要求您做的第一件事是检查
mmap
是否成功。它可能失败了,您可能正试图读取/写入不可访问的内存。最后,您需要从
asm\u save\u main\u和\u go
附加一个调试器和单步,并检查它到底在哪里失败。@AjayBrahmakshatriya如果
movq 8(%rsi),%rsp
真的是SIGSEGV,那就不能是对齐问题,只需将
mov
转换为
rsp
即可。对齐将在以后引起问题。所以你关于无效内存的观点更有可能。但是我希望它已经在
movq(%rsi),%rbx
.Oh和
pContexts[0]崩溃了mmap
堆栈大小
。虽然它在技术上是正确的,但如果堆栈完全为空,
[rsp]
仍然被视为“堆栈顶部值”,并且仅出于某些检查目的而达到该值的任何代码都将到达
mmap
区域之外,我宁愿将一些
nullptr
或“canary”值放入新堆栈中,因此,它将指向
stack\u region.end()-8
地址,而不是确切地指向
end()
@Ped7g。我不认为是该指令导致了问题。好吧,没有理由指令会失败。就像OP提到的那样,他通过观察代码在没有指令的情况下运行良好来挑出指令。所以这一定是因为这个指令而发生的事情。这里的不对准肯定会导致像libc这样的系统崩溃。