如何用C ++给定的地址覆盖汇编程序堆栈的返回地址?

时间:2019-05-18 17:57:44

标签: c++ assembly x86 coroutine context-switch

我有一个函数switchContext(void*& from, void*& to)。它接收两个堆栈指针,并应更改进程的上下文。因此,如果我得到了协程A且它使用了特定功能resume(),协程B将会继续工作。 目前,我的代码无法正常工作。

我正在使用Nasm和GCC进行编译。

汇编程序:(switchContext(void*& from, void*& to)):

switchContext:

    ; epb = esp
    mov     ebp, esp

    ; save registers
    push    ebp
    push    ebx
    push    esi
    push    edi

    ; from <= returnadress
    ; eax <= returnadress
    mov     eax, [ebp+16] 
    mov     edx, ebp
    add     edx, 20 ; make edx point to 'from'
    ; overwrite 'from' with returnadress
    mov     [edx], eax

    ; what to do now: returnadress <= to
    ; eax <= to
    mov     eax, [ebp+24]
    mov     edx, ebp
    add     edx, 16 ; make edx point to returnadress
    ; overwrite returnadress with 'to'
    mov     [edx], eax

    pop     edi ; RSA = esp + 12
    pop     esi ; RSA = esp + 8
    pop     ebx ; RSA = esp + 4
    pop     ebp ; RSA = esp + 0

    ; use new returnadress to jump to 'to'
    ret     

这是对应的C ++类:

extern "C" {
    void switchContext(void*& from, void*& to);
}

class Coroutine {
public:

    const char* name;
    Coroutine(void* tos = 0)
    {
        setup(tos);
    }

    void resume(Coroutine* next)
    {

        switchContext(this->sp, next->sp);
    }

    virtual void body() = 0;
    virtual void exit() = 0;

private:
    static void startup(Coroutine* obj) {
        obj->body();
        obj->exit();
    };

    void setup(void* tos) {
        if (tos == 0) {
            unsigned temp_stack[1024];
            this->sp = &temp_stack;
            return;
        }

        this->sp = &tos;
        return;
    };

    void* sp;
};

当前,我的程序崩溃了。但这只能通过在汇编程序中用“ to”覆盖返回地址来实现。

在此过程中我在哪里出错?

1 个答案:

答案 0 :(得分:3)

您的mov ebp,esp放在错误的位置。应该在寄存器保存之后。

您没有取消引用参考。从汇编程序的角度来看,C ++引用只是一个指针,因此您的参数为void **。由于您想将返回地址保存/加载到所指向的地址,因此需要一个额外的间接方式将值保存到所指向的地址。

与此问题无关:您使用edx进行的某些地址计算可以简化为更少的指令。您也可以省去使用ebp并使用基于esp的偏移量。