C++跳转到其他方法执行

在我的 C++ JNI-Agent 项目中,我正在实现一个函数,该函数将被赋予可变数量的参数并将执行传递给另一个函数:

In my C++ JNI-Agent project i am implementing a function which would be given a variable number of parameters and would pass the execution to the other function:

// 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;
}

上面的函数只需要跳转到:

The function above needs to just jump to the:

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
}

上面的代码完美运行,原函数可以正确读取所有参数(测试了包括数组在内的9个不同类型的参数).

The code above works perfectly, the original function can read all the parameters correctly (tested with 9 parameters of different types including arrays).

但是,在从拦截器跳转到原始函数之前,我需要进行一些计算.但是,在这里我观察到有趣的行为.

However, before jumping into original function from the interceptor i need to do some computations. However, here i observe interesting behavior.

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;
}

这仍然可以正常工作,我能够进行一些基本的计算,然后重置堆栈指针并跳转到我的原始函数,原始函数也正确地从 var_args 读取参数.但是:如果我用 mallocprintf("any string"); 替换基本的 int 操作,那么,不知何故,如果跳转到我的原始函数,那么我的参数得到搞砸了,原始函数结束读取错误的值...

This still works fine, i am able to do some basic computations , then reset the stack pointer and jump to my original function, the original function also reads the parameters from the var_args correctly. However: if i replace the basic int operations with malloc or printf("any string"); , then, somehow, if jump into my original function, then my parameters get messed up and the original function ends reading wrong values...

我已尝试调试此行为,并检查了内存区域以查看发生了什么问题...在跳转之前,一切看起来都很好,ebp 后面是函数参数.

I have tried to debug this behavior and i inspected the memory regions to see what is goin wrong... Right before the jump, everything looks fine there, ebp is being followed by function parameters.

如果我 在没有复杂计算的情况下跳转,一切正常,ebp 后面的内存区域不会改变.原始函数读取正确的值...

现在如果我在执行 printf 后跳转(例如),原始方法读取的参数会损坏...

是什么导致了这种奇怪的行为?printf 甚至没有在我的方法中存储任何 lokal 变量...好吧,它确实在寄存器中存储了一些文字,但是为什么我的堆栈仅在跳转之后才损坏而不是在它之前?

What is causing this strange behavior? printf doesnt even store any lokal variables in my method... Ok it does store some literals in registers but why my stack gets corrupted only after the jump and not already before it?

对于这个项目,我使用在 Windows 机器上运行的 g++ 版本 4.9.1 编译器.

For this project I use g++ version 4.9.1 compiler running on a windows machine.

是的,我担心 std::forward 和模板选项,但它们在我的情况下不起作用...... Aaand 是的,我知道跳入其他方法有点笨拙,但这是我唯一的想法如何带来JNI 拦截器工作...

And yes I am concerned of std::forward and templates options but they just do not work in my case... Aaand yes I know that jumping into other methods is a bit hacky but thats my only idea of how to bring JNI-interceptor to work...

********************编辑********************

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

如前所述,我将生成的汇编代码与源函数一起添加.

As discussed i am adding the generated assembler code with the source functions.

没有 printf 的函数(工作正常):

Function without printf (which works fine):

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
}

现在 asm 输出 printf 变体...

Now asm output for printf variant...

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
}

这里是 printf 函数的 asm 代码:??

And here is the asm code for the printf function:

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

看起来 printf 对 rbp 做了很多操作,但我看不出有什么问题...

It looks like printf does many operations on rbp , but i cannot see anything wrong with it...

这里是截获函数的asm代码.

And here is the asm code of the intercepted function.

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)

****************** 编辑 2 **************

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

我认为在运行时查看内存如何变化会很有用:

I thought it would be useful to see how memory changes at the run-time:

第一张图是进入拦截器函数后的内存布局:

The first picture shows the memory layout right after entering the interceptor function:

第二张图片在有问题的代码(如 printf 等)之后显示了相同的内存区域

The second images shows the same memory region after problematic code (like printf and so)

第三张图是跳转到原函数后的内存布局.

The third picture shows the memory layout right after jumping to original function.

如您所见,调用 printf 后,堆栈看起来很好,但是当我跳转到原始函数时,它搞砸了......

As you can see, right after calling printf , stack looks fine, however when i jump into the original function, it messes up...

看截图,我很确定所有参数都在内存中的堆栈上,并且参数不是通过寄存器传递的.

Looking at the screenshots, I am pretty sure that all the parameters lie on the stack in the memory, and parameter are not passed by registers.

推荐答案

使用集合调用约定在程序集中手动传递参数.在这种情况下,参数在以 %rcx 开头的寄存器中传递.对用作调用约定的寄存器的任何修改都会改变任何进行的 jmp 所感知的参数.

Arguments are passed manually in assembly using a set calling convention. In this case, the arguments are passed in registers beginning with %rcx. Any modification to the registers used as calling conventions will change the arguments perceived by any proceeding jmp.

在 jmp 之前调用 printf 会将 %rcx 的值从 *env 更改为指向常量的指针你好".更改 %rcx 的值后,您需要将其恢复为之前的值.以下代码应该可以工作:

Calling printf before your jmp changes the value of %rcx from *env to a pointer to constant "hello". After you change the value of %rcx you need to restore it to the value it was previously. The following code should work:

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

//just an example
int x=8;

printf("hey");

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

// restore %rcx to equal *env
asm volatile("mov %rcx, 0x10(%rbp)");

// 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;

}

相关文章