c/cpp暗改调用栈的一些玩法(0x01)

调用栈浅说

调用栈的图参考了这篇博客https://blog.csdn.net/VarusK/...
通过这个图可以形象看出函数调用的过程中,每个函数调用的栈帧如何,栈区的排布等信息。

当一个函数被调用的时候,进栈的顺序为:

  1. 主函数中函数调用后的下一条指令(函数调用语句的下一条可执行语句)的地址。
  2. 函数的各个参数,由右往左入栈的,然后是函数中的局部变量。
    c/cpp暗改调用栈的一些玩法(0x01)_第1张图片

修改调用栈中的返回地址

如上面一小节提到的那样,我们的传参和主调函数的下一个指令地址在栈的结构中是相邻的,于是我们可以通过参数地址定位到主调函数的下一指令地址,并对其进行修改。

代码如下:

#include 

#define dp(msg)\
{\
    printf(msg);\
    printf(" > in func: < %s >", __FUNCTION__);\
    printf("\n");\
}

void leaper( int a )
{
    int* p    = &a;
    if ( a ){
        for( int i = 0; i < 64; i ++ ){
            if ( ( *(p+i) > (long)leaper ) && ( ( *(p+i) - (long)leaper ) < 0x1000 ) ){
                *(p+i) += 5;
                break;
            }
            if ( ( *(p+i) < (long)leaper ) && ( ( (long)leaper - *(p+i) ) < 0x1000 ) ){
                *(p+i) += 5;
                break;
            }
        }
    }
    return;
}

void func_a()
{
    dp("here!");
}

int main( int argc, char* argv[] )
{
    leaper(1);
    func_a();
    dp("end");
    return 0;
}

代码说明:main中调了leaper函数,首先会把执行func_a的指令地址(也就是执行完leaper之后的指令地址)先压栈,然后将传参压栈,在leaper中参数地址取出来&a,然后在其周围寻找类似于leaper指针值的值,找到第一个就ok,然后将其+5,这就算给下一条指令地址加上偏移了,这里面我担心不同编译器的栈帧结构不太一样,所以采取这种写法容错高一些,+5是为了正确跳过指令的长度,(不同的芯片这个值不一样,这样看具体的指令长度,比如arm应该是4,86的芯片的call指令写5应该是ok,具体应该怎么样可以使用objdump看汇编代码,然后进行修改)。

最终的效果,因为调用栈中的下一条指令的地址被修改了,所以func_a会被跳过。直接打印"end"了。

因为c/cpp有了指针,会导致有很多玩内存的玩法,通常会造成一些让人错愕的效果,“不安全”和“玩法”成为了c/cpp这枚硬币的两面。

你可能感兴趣的:(cc++操作系统asm编译器)