.首先认识由c/c++编译的程序占用的内存有哪些部分:    

            C语言探索之栈帧       C语言探索之栈帧               

     1.其中堆区与栈区是相对而生的。(之后研究栈帧会将栈区单独列出来)

     2.在程序运行期间,地址空间区是一直存在的,程序退出,地址空间也随即释放。

二.其次需要认识一个数据结构以及两个汇编命令。

     1.认识一个数据结构:栈。

     栈有栈底和栈顶,是一种“先进后出”的结构。

      2. 两个汇编命令:pushpop

         从栈顶入栈称为push

         从栈底出栈称为pop

         其中入栈和出栈都是在同一位置进行的。

三.接下来了解CPU中需要用到的一些寄存器。

      CPU中:读取指令(内存-->CPU-->分析指令(CPU-->执行指令(CPU

      1. 通用寄存器:EAX EBX ECX EDX

      2. EIPpc):程序计数器(用来存放当前正在执行指令的下一条指令的地址)

      3. ESP:栈顶

      4. EBP:栈底

四.现在开始研究栈帧。

1. 研究栈帧,接下来会将其从地址空间里面单独列出。

2. VC6.0下通过此代码研究栈帧:

#include <stdio.h>

#include <windows.h>

int myadd(int a,int b)

{

int c = a+b;

return c;

}

int main()

{

int a = 0xAAAAAAAA;

int b = 0xBBBBBBBB;

int ret = myadd(a,b);

printf("%d\n",ret);

system("pause");

return 0;

}

3. 现在要做的是:Fn+F11进行调试,然后将其转换成汇编语言,打开寄存器和内存。

  main()函数

(1)进入main(),首先会根据该函数内部临时变量的大小为它开辟空间(也就是    main的栈帧)

                                        C语言探索之栈帧

(2) 开始对main()进行一系列操作

        C语言探索之栈帧

汇编意思:

12行:将0xAAAAAAAAa)放入ebp-4的位置(即为a开辟4字节空间,其&a = ebp-4

13行:将0xBBBBBBBBb)放入ebp-8的位置(即为b开辟4字节空间,且在a下方)

寄存器与内存的情况:

 C语言探索之栈帧

在栈帧中:

                     C语言探索之栈帧


     临时变量:

     (1)形成a、b的临时变量   

        C语言探索之栈帧

   汇编解释:将b存在eax中,然后b入栈

          a存在ecx中,然后a入栈

         (注意:先传入的是b,然后才是b

   寄存器和内存的变化:

       b形成临时变量:EAX esp以及esp的内容均发生了变化

    C语言探索之栈帧

        a形成临时变量:继续观察EAX esp以及esp的内容

      C语言探索之栈帧

  在栈中:观察如何创建临时变量:

           将变量(a或者b)放在EAX中,EAX入栈,esp下移4字节(int所占空间的大小)。

                    C语言探索之栈帧

        结论:在传递变量是从左往右传,形成临时变量是从右向左。                  

(2)现在是进入被调用函数的准备工作:

      1)  执行时:注意观察esp(执行现在esp0019FEE0)的变化,以及之后一步EIP的变化

        C语言探索之栈帧

          寄存器和内存的情况:

       2)esp的变化(将现在执行指令的下一条指令 00401093 进行了入栈操作)

     C语言探索之栈帧

      3)现在观察EIP的变化:跳转到了目标函数的入口。

        C语言探索之栈帧

       4)call命令的执行分为两步:

          1) 将当前执行指令的下一条指令入栈。

          2) 跳转至目标函数,也就是修改EIP的值。

      5) 在栈帧中的情况:

                             C语言探索之栈帧         

    (3)myadd()入栈操作,形成它的栈帧

         C语言探索之栈帧

      汇编含义:ebp入栈--->ebp指向esp的位置--->esp-44h形成myadd()栈帧。

          寄存器和内存的变化:ebp入栈:esp下移将ebp的值放入。

                   C语言探索之栈帧

                                  现在是将ebp指向esp指向的位置,esp-44h创建了myadd()的栈帧

                  C语言探索之栈帧

           在栈帧中的情况:

                                     C语言探索之栈帧

对myadd()的调用

     (1)调用后对函数的的执行

                 C语言探索之栈帧

      汇编含义:将ebp+8(即a)放入eax中,将b+a的值放入eax中,将eax放在ebp-4处。

                       在将ebp-4放入eax中。

      寄存器和内存的变化:

      EAX放入a的值

               C语言探索之栈帧 

          EAX放入a+b的值

         C语言探索之栈帧

       将EAX入栈

              C语言探索之栈帧

          在栈帧中的情况:

                           C语言探索之栈帧

      (2)myadd()的出栈过程。

              C语言探索之栈帧

             汇编含义:

                  esp指向ebp所指的位置(即释放了myadd()的栈帧,不过它只是认为可以其他覆盖,所以当刚刚释放完之后还是              可以找到其内部的变量)

            ebp出栈:返回到刚才存入mai_ebp的位置(即回到main()栈顶)

                C语言探索之栈帧

      开始执行ret指令:

                C语言探索之栈帧

          可以看出:ret执行执行两点:

                    1) 将当前esp所指向的内容(即main()的返回值地址出栈)

                    2) 然后用其修改EIP

            栈帧的情况:

                            C语言探索之栈帧     

      (3)返回到main()函数

               C语言探索之栈帧

          汇编含义:

                       esp+8即指向mainesp,也就是释放了临时变量的空间。

              将eax移到ebp-ch的位置,即ret的位置。

                栈帧的情况:

                                 C语言探索之栈帧

          以上便完成了一次完整的函数调用。

五.利用已知的栈帧结构改变函数的返回位置:

   main()--> myadd() -->  bug()--> main()的过程。

    由栈帧结构可以知道:call中保存了返回函数的地址,所以只要将这一地址改为bug()的入口地址即可。

       C语言探索之栈帧

执行代码以及代码分析

#include <stdio.h>

#include <windows.h>


void* g_ret = NULL;


void bug()
{
int x = 0;
int *p = &x; //取x的地址,将其放在指针变量P中
p += 2;      //找到恢复函数的地址
*p = (int) g_ret;//将之前最开始返回的地址有放回原处,保证可以回到main函数
printf("i am a bug!haha...you run here!\n");
system("pause");
}


int myadd(int a,int b)
{
int *p = &a; //定义一个指针变量p,使他指向a
int c = 0;
p--;         //p-1到达要返回地址
    g_ret =(void*) *p;//将它存在g_ret中
*p = (int)bug;    //将其接受bug的地址,待会跳转至bug函数
c = a+b;
printf("myadd begin run!...\n");
return c;
}


int main()
{
int a = 1;
int b = 2;
int ret = 0;
printf("main gegin run!...\n");
ret = myadd(a,b);
printf("you should run here! \n");
_asm{               //bug函数是通过修改地址进入的,没有执行call命令,则它少一个地址的push过                                程,而调完之后依旧执行了pop指令,则是的esp大了4
sub esp,4    //则需要将其-4以保证栈的平衡性
}
system("pause");
return 0;

}

C语言探索之栈帧

相关文章:

  • 2021-08-11
  • 2021-10-23
  • 2021-04-27
  • 2021-10-20
  • 2022-02-20
  • 2021-07-08
  • 2021-10-18
  • 2022-12-23
猜你喜欢
  • 2021-10-24
  • 2021-10-20
  • 2021-07-25
  • 2021-04-09
  • 2021-11-13
  • 2021-07-19
相关资源
相似解决方案