C ++ salta para outra execução de método

No meu projeto C ++ JNI-Agent, estou implementando uma função que receberia um número variável de parâmetros e passaria a execução para a outra função:

// address of theOriginalFunction
public static void* originalfunc;

void* interceptor(JNIEnv *env, jclass clazz, ...){

    // add 4 to the function address to skip "push ebp / mov ebp esp"
    asm volatile("jmp *%0;"::"r" (originalfunc+4));

    // will not get here anyway
    return NULL;
}

A função acima precisa apenas pular para:

JNIEXPORT void JNICALL Java_main_Main_theOriginalFunction(JNIEnv *env, jclass clazz, jboolean p1, jbyte p2, jshort p3, jint p4, jlong p5, jfloat p6, jdouble p7, jintArray p8, jbyteArray p9){
    // Do something
}

O código acima funciona perfeitamente, a função original pode ler todos os parâmetros corretamente (testado com 9 parâmetros de diferentes tipos, incluindo matrizes).

No entanto, antes de pular para a função original do interceptor, preciso fazer alguns cálculos. No entanto, aqui eu observo um comportamento interessante.

void* interceptor(JNIEnv *env, jclass clazz, ...){
    int x = 10;
    int y = 20;
    int summ = x + y;

    // NEED TO RESTORE ESP TO EBP SO THAT ORIGINAL FUNCTION READS PARAMETERS CORRECTLY
    asm (
        "movl %ebp, %esp;"
        "mov %rbp, %rsp"
    );

    // add 4 to the function address to skip "push ebp / mov ebp esp"
    asm volatile("jmp *%0;"::"r" (originalfunc+4));

    // will not get here anyway
    return NULL;
}

Isso ainda funciona bem, eu sou capaz de fazer alguns cálculos básicos, redefinir o ponteiro da pilha e pular para a minha função original, a função original também lê os parâmetros do var_args corretamente. No entanto: se eu substituir as operações int básicas pormalloc ouprintf("any string"); , então, de alguma forma, se pular para minha função original, meus parâmetros serão alterados e a função original terminará lendo valores errados ...

Eu tentei depurar esse comportamento e inspecionei as regiões da memória para ver o que está acontecendo de errado ... Logo antes do salto, tudo fica bem lá, ebp está sendo seguido por parâmetros de função.

Se eupule sem cálculos complicados, tudo funciona bem, a região da memória atrás do ebp não muda. função original lê valores corretos...

Sem wi-fipular após executar printf (por exemplo), os parâmetros lidos pelo método original são corrompidos...

O que está causando esse comportamento estranho? printf nem armazena nenhuma variável lokal no meu método ... Ok, ele armazena alguns literais nos registros, mas por que minha pilhaé corrompido somente após o salto e ainda não antes?

Para este projeto, eu uso o compilador g ++ versão 4.9.1 em execução em uma máquina Windows.

E sim, eu estou preocupado com as opções std :: forward e templates, mas elas simplesmente não funcionam no meu caso ... E sim, eu sei que pular para outros métodos é um pouco hacky, mas essa é minha única idéia de como trazer o JNI-interceptor trabalhar...

******************** EDIT ********************

Como discutido, estou adicionando o código do assembler gerado com as funções de origem.

Função sem printf (que funciona bem):

void* interceptor(JNIEnv *env, jclass clazz, ...){

    //just an example
    int x=8;

    // restoring stack pointers
    asm (
        "movl %ebp, %esp;"
        "mov %rbp, %rsp"
    );

    // add 4 to the function address to skip "push ebp / mov ebp esp"
    asm volatile("jmp *%0;"::"r" (originalfunc+4));

    // will not get here anyway
    return NULL;
}

void* interceptor(JNIEnv *env, jclass clazz, ...){
    // first when interceptor is called, probably some parameter restoring...
    push %rbp
    mov %rsp %rbp
    sub $0x30, %rsp
    mov %rcx, 0x10(%rbp)
    mov %r8, 0x20(%rbp)
    mov %r9, 0x28(%rbp)
    mov %rdx, 0x18(%rbp)

    // int x = 8;
    movl $0x8, -0x4(%rbp)

    // my inline asm restoring stack pointers
    mov %ebp, %esp
    mov %rbp, %rsp

    // asm volatile("jmp *%0;"::"r" (originalfunc+4))
    mov 0xa698b(%rip),%rax      // store originalfunc in rax
    add %0x4, %rax
    jmpq *%rax

    // return NULL;
    mov $0x0, %eax
}

Agora saída ASM para a variante printf ...

void* interceptor(JNIEnv *env, jclass clazz, ...){

    //just an example
    int x=8;

    printf("hey");

    // restoring stack pointers
    asm (
        "movl %ebp, %esp;"
        "mov %rbp, %rsp"
    );

    // add 4 to the function address to skip "push ebp / mov ebp esp"
    asm volatile("jmp *%0;"::"r" (originalfunc+4));

    // will not get here anyway
    return NULL;
}

void* interceptor(JNIEnv *env, jclass clazz, ...){
    // first when interceptor is called, probably some parameter restoring...
    push %rbp
    mov %rsp %rbp
    sub $0x30, %rsp
    mov %rcx, 0x10(%rbp)
    mov %r8, 0x20(%rbp)
    mov %r9, 0x28(%rbp)
    mov %rdx, 0x18(%rbp)

    // int x = 8;
    movl $0x8, -0x4(%rbp)

    // printf("hey");
    lea 0x86970(%rip), %rcx   // stores "hey" in rcx???
    callq 0x6b701450          // calls the print function, i guess

    // my inline asm restoring stack pointers
    mov %ebp, %esp
    mov %,rbp, %rsp

    // asm volatile("jmp *%0;"::"r" (originalfunc+4))
    mov 0xa698b(%rip),%rax      // store originalfunc in rax
    add %0x4, %rax
    jmpq *%rax

    // return NULL;
    mov $0x0, %eax
}

E aqui está o código asm para a função printf:

printf(char const*, ...)
    push %rbp
    push %rbx
    sub $0x38, %rsp
    lea 0x80(%rsp), %rbp
    mov %rdx, -0x28(%rbp)
    mov $r8, -0x20(%rbp)
    mov $r9, -0x18(%rbp)
    mov $rcx, -0x30(%rbp)
    lea -0x28(%rbp), %rax
    mov %rax, -0x58(%rbp)
    mov -0x58(%rbp), %rax
    mov %rax, %rdx
    mov -0x30(%rbp), %rcx
    callq 0x6b70ff60 // (__mingw_vprintf)
    mov %eax, %ebx
    mov %ebx, %eax 
    add $0x38, %rsp
    pop %rbx
    pop %rbp
    retq

Parece que o printf faz muitas operações no rbp, mas não consigo ver nada de errado com ele ...

E aqui está o código asm da função interceptada.

push %rbp              // 1 byte
push %rsp, %rbp        // 3 bytes , need to skip them
sub $0x50, %rsp
mov %rcx, 0x10(%rbp)
mov %rdx, 0x18(%rbp)
mov %r8d, %ecx
mov %r9d, %edx
mov 0x30(%rbp), %eax
mov %cl, 0x20(%rbp)
mov %dl, 0x28(%rbp)
mov %ax, -0x24(%rbp)

************* EDIT 2 **************

Eu pensei que seria útil ver como a memória muda no tempo de execução:

A primeira imagem mostra o layout da memória logo após a entrada na função de interceptador:

As segundas imagens mostram a mesma região de memória após o código problemático (como printf e outros)

A terceira imagem mostra o layout da memória logo após pular para a função original.

Como você pode ver, logo após chamar printf, a pilha fica bem, no entanto, quando pulo para a função original, ela erra ...

Observando as capturas de tela, tenho certeza de que todos os parâmetros estão na pilha da memória e os parâmetros não são passados pelos registradores.

questionAnswers(3)

yourAnswerToTheQuestion