C ++跳转到其他方法执行 [英] C++ jump to other method execution

查看:75
本文介绍了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后面的内存区域不会更改.原始函数读取正确的值 ...

If i jump without complicated computations, everything works fine, memory region behind ebp doesnt get changed. original function reads correct values...

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

Now if i jump after doing printf (for example), the parameters read by the original method get corrupted...

是什么导致这种奇怪的行为? 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和template选项,但是它们在我的情况下不起作用... 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;

}

这篇关于C ++跳转到其他方法执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆