posts - 225, comments - 62, trackbacks - 0, articles - 0
   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

首先需要读者了解x86平台上的函数调用及返回机制

通过以上我们了解了函数的参数、函数中的局部变量、函数的返回地址都在堆栈中,那么通过操作堆栈就可以控制函数的返回点,而且我们可以精心构造堆栈来传递函数参数。

来看这样一个例子

   1:  #include <stdio.h>
   2:   
   3:  void func2()
   4:  {
   5:      puts("func2");
   6:  }
   7:   
   8:  void func1()
   9:  {
  10:      puts("func1");
  11:  }
  12:   
  13:  int main()
  14:  {
  15:      __asm
  16:      {
  17:          push lable1;
  18:          push func2;
  19:          jmp func1;
  20:      }
  21:  lable1:
  22:      puts("after lable1");
  23:   
  24:      scanf("%*s");
  25:  }

输出的结果是

func1
func2
after lable1

我们在jmp到func1之前构造了堆栈,让func1返回时取出的返回地址是func2的首地址,因此在执行完func1之后自动跳转到另func2执行,而执行完func2之后又取出了我们压入的lable1,所以返回了回来。

再来看一个稍微复杂点的例子,还是通过操作堆栈。我们在两个函数直接来回跳转。

Func1使用的是操作系统分配的堆栈,而Func2使用的是利用数组char stack2[STACK_SIZE]模拟的堆栈。

   1:  #include <stdio.h>
   2:   
   3:  void* esp1;
   4:  void Func1();
   5:   
   6:  #define STACK_SIZE 0x80000
   7:  char stack2[STACK_SIZE];
   8:  void* esp2 = stack2+STACK_SIZE;
   9:  void Func2();
  10:   
  11:  __declspec( naked )
  12:  void SetStack2()
  13:  {
  14:      __asm
  15:      {
  16:          push ebp
  17:          mov ebp,esp
  18:          sub esp, __LOCAL_SIZE
  19:   
  20:          mov esp, esp2
  21:          push Func2
  22:          pushad
  23:   
  24:          mov esp2, esp
  25:          mov esp,ebp
  26:          pop ebp
  27:          ret
  28:      }
  29:  }
  30:   
  31:  __declspec ( naked )
  32:  void SwitchToFunc1()
  33:  {
  34:      __asm
  35:      {
  36:          pushad
  37:          mov esp2, esp
  38:          mov esp, esp1
  39:          popad
  40:          ret
  41:      }
  42:  }
  43:   
  44:  __declspec ( naked )
  45:  void SwitchToFunc2()
  46:  {
  47:      __asm
  48:      {
  49:          pushad
  50:          mov esp1, esp
  51:          mov esp, esp2
  52:          popad
  53:          ret
  54:      }
  55:  }
  56:   
  57:  void Func1()
  58:  {
  59:      while(1)
  60:      {
  61:          puts("func1");
  62:          SwitchToFunc2();
  63:      }
  64:  }
  65:   
  66:  void Func2()
  67:  {
  68:      while(1)
  69:      {
  70:          puts("func2");
  71:          SwitchToFunc1();
  72:      }
  73:  }
  74:   
  75:  int main()
  76:  {
  77:      SetStack2();
  78:      Func1();
  79:   
  80:      scanf("%*s");
  81:  }

如果我们把SetStack2写得更通用一些,比如将函数指针通过参数传递进去,在把eps1,eps2做成一个环状链表,这样SwitchToFunc1/SwitchToFunc2也可以合并成在环状链表上切换。

这样就只要SetStack在加入到环状链表中就好像创建了一个线程一样,通过Switch函数切换线程的控制权。具体的代码就不贴出来了,大家可以

只有注册用户登录后才能发表评论。